链路跟踪Jaeger使用总结

本文涉及的产品
检索分析服务 Elasticsearch 版,2核4GB开发者规格 1个月
简介: 链路跟踪Jaeger使用总结

背景

当系统架构变得越来越复杂后,我们一次前端请求,有可能要经历跨多个线程/跨多个协程/跨多个进程处理后,才会最终响应到客户端,如果请求按照预期正确执行还好,万一在某个调用链的某一环节出现了问题,排查起来非常的麻烦,但如果有链路跟踪的话,就会大大降低排查的困难度。

可以通过在关键点设置链路埋点,记录下重要的步骤,例如,请求接收处,逻辑处理处,数据库交互处,调用了外部服务等。

在阅读本文章之前,大家需要有链路跟踪的基础知识,这个可以查看我另一篇文章:我眼中的OpenTracing

前言

Jaeger是一款广受欢迎的开源分布式链路跟踪系统,兼容OpenTracing API,且已加入CNCF开源组织。其主要功能是聚合来自各个异构系统的实时监控数据。对一些常用的框架通过插件可以达到无侵入式跟踪,比如Apache HttpClient,Elasticsearch,JDBC,Kafka,Memcached,Mongo,OkHttp,Redis,Spring Boot,Spring Cloud,要通过Jaeger将Java应用数据上报至链路追踪控制台,首先需要完成埋点工作。您可以手动埋点,也可以利用各种现有插件实现埋点的目的。本文介绍以下三种埋点方法的其中一种手动埋点。

  1. 手动埋点
  2. 通过Spring Cloud组件埋点
  3. 通过gRPC组件埋点

一、Jaeger 是什么?

由于 Uber 的业务增长迅猛,其软件架构也越来越复杂,截止 2015 年下半年,Uber 内部已经有 500 多个微服务在运行,给问题排查和性能分析带来巨大困难。2016 年 4 月,Uber 启动 Jaeger 项目,并逐渐在内部推行分布式追踪系统,一年之后(2017 年 4 月),Uber 宣布正式将 Jaeger 开源。Uber Engineering Blog 有一篇文章介绍了分布式追踪系统在 Uber 的演进过程,建议阅读,《Evolving Distributed Tracing at Uber Engineering》。Uber开源的Jaeger(发音为ˈyā-gər ),因为它对OpenTracing支持的比较好,而且部署使用也非常简单。另外Jaeger的作者就是Yurishkuro。这里就不介绍Jaeger的细节了,有兴趣的可以去官网了解:Jaeger官网

按照数据流向,整体可以分为四个部分:

jaeger-client:Jaeger 的客户端,实现了 OpenTracing 的 API,支持主流编程语言。客户端直接集成在目标 Application 中,其作用是记录和发送 Span 到 Jaeger Agent。在 Application 中调用 Jaeger Client Library 记录 Span 的过程通常被称为埋点。

jaeger-agent:暂存 Jaeger Client 发来的 Span,并批量向 Jaeger Collector 发送 Span,一般每台机器上都会部署一个 Jaeger Agent。官方的介绍中还强调了 Jaeger Agent 可以将服务发现的功能从 Client 中抽离出来,不过从架构角度讲,如果是部署在 Kubernetes 或者是 Nomad 中,Jaeger Agent 存在的意义并不大。

jaeger-collector:接受 Jaeger Agent 发来的数据,并将其写入存储后端,目前支持采用 Cassandra 和 Elasticsearch 作为存储后端。个人还是比较推荐用 Elasticsearch,既可以和日志服务共用同一个 ES,又可以使用 Kibana 对 Trace 数据进行额外的分析。架构图中的存储后端是 Cassandra,旁边还有一个 Spark,讲的就是可以用 Spark 等其他工具对存储后端中的 Span 进行直接分析。

jaeger-query & jaeger-ui:读取存储后端中的数据,以直观的形式呈现。

Jaeger 的架构非常清晰,部署起来也很轻松,Docker Hub 中有官方打好的 Image,可以拿来直接用,https://hub.docker.com/u/jaegertracing/。如果是本地测试,可以直接用 Jaeger 的 all-in-one Image,


上部分 Agent :负责从应用中,收集链路信息,发送给 SkyWalking OAP 服务器。目前支持 SkyWalking、Zikpin、Jaeger 等提供的 Tracing 数据信息。而我们目前采用的是,SkyWalking Agent 收集 SkyWalking Tracing 数据,传递给服务器。

Jaeger receiver

Jaeger receiver 目前只在跟踪模式下工作,不支持分析模式。Jaeger receiver提供额外的GRPC主机/端口,如果没有,将使用共享服务器主机/端口,还没有就使用核心GRPC主机/端口。Receiver需要激活Jaeger ElasticSearch存储实现。阅读此内容了解如何激活。right now only works in Tracing Mode, and no analysis. Jaeger receiver provides extra gRPC host/port, if absent, sharing-server host/port will be used, then core gRPC host/port. Receiver requires jaeger-elasticsearch storage implementation active. Read this to know how to active.

现在,你需要jaeger agent 来批量发送spans到 SkyWalking的oap服务器。 阅读Jaeger Architecture获取更多详情。

激活这个receiver。

receiver_jaeger:
  default:
    gRPCHost: ${SW_RECEIVER_JAEGER_HOST:0.0.0.0}
    gRPCPort: ${SW_RECEIVER_JAEGER_PORT:14250}
  • 下部分 SkyWalking OAP :负责接收 Agent 发送的 Tracing 数据信息,然后进行分析(Analysis Core) ,存储到外部存储器( Storage ),最终提供查询( Query )功能。
  • 右部分 Storage :Tracing 数据存储。目前支持 ES、MySQL、Sharding Sphere、TiDB、H2 多种存储器。而我们目前采用的是 ES ,主要考虑是 SkyWalking 开发团队自己的生产环境采用 ES 为主。
  • 左部分 SkyWalking UI :负责提供控台,查看链路等等。

二、使用步骤

1.引入库

api group: 'io.jaegertracing', name: 'jaeger-client', version: '1.6.0';
    api group: 'io.opentracing', name: 'opentracing-api', version: '0.33.0';
    api group: 'io.opentracing', name: 'opentracing-util', version: '0.33.0';

2.配置初始化参数

public void init(String serverName, String agentIp, int agentPort, int ratingLimit) {
        logger.info("init tracer client start,serverName={},agentIp={},agentPort={}", serverName, agentIp, agentPort);
        Configuration config = new Configuration(serverName);
        SenderConfiguration sender = new SenderConfiguration();
        sender.withAgentHost(agentIp);
        sender.withAgentPort(agentPort);
        //一秒钟最多1000次采样
        if (ratingLimit == 0) {
            ratingLimit = 1000;
        }
        config.withSampler(new SamplerConfiguration().withType(RateLimitingSampler.TYPE).withParam(ratingLimit));
        //全量采集,采样率设置0,1 分别对应打开和关闭
        //  config.withSampler(new SamplerConfiguration().withType(ConstSampler.TYPE).withParam(1));
        config.withReporter(new ReporterConfiguration().withSender(sender).withMaxQueueSize(10000).withFlushInterval(60000));
        config.withCodec(new CodecConfiguration().withBinaryCodec(Format.Builtin.BINARY, new BinaryCodec()));
        GlobalTracer.registerIfAbsent(config.getTracer());
        logger.info("init tracer client end,serverName={},agentIp={},agentPort={}", serverName, agentIp, agentPort);
    }

如何设置埋点?

/**
     * 开启链路
     */
    public Span startTrace(SpanContext context, String methodName) {
        if (methodName == null) {
            return null;
        }
        Tracer tracer = GlobalTracer.get();
        SpanBuilder spanBuilder = tracer.buildSpan(methodName);
        String traceId = ThreadContext.get(SystemConstant.TRACE_ID);
        if (traceId != null) {
            spanBuilder.withTag("userId", traceId);
        }
        String threadName = "";
        if (Thread.currentThread().isVirtual()) {
            spanBuilder.withTag(SystemConstant.VIRTUAL_KEY, true);
            threadName = "[" + Thread.currentThread().getName() + "]";
        } else {
            spanBuilder.withTag(SystemConstant.VIRTUAL_KEY, false);
            threadName = Thread.currentThread().getName();
        }
        spanBuilder.withTag(SystemConstant.THREAD_KEY, threadName);
        //  Tags.SPAN_KIND_SERVER;
        if (context != null) {
            spanBuilder.asChildOf(context);
        }
        Span span = spanBuilder.start();
        return span;
    }

//埋点

Span span = TraceService.getInstance().startTrace(methodName);
        try (Scope scope = GlobalTracer.get().activateSpan(span)) {
            span.setTag("message", this.getName());
           //handle your business
        }  catch (Exception e) {
            TracingHelper.onError(e, span);
        } finally {
            span.finish();
        }

批量上报的线程如下:

上报的类 :io.jaegertracing.internal.reporters.RemoteReporter

上报的方法在io.jaegertracing.internal.reporters.RemoteReporter.QueueProcessor#run

如果要把上报的日志打开

则要做如下设置:

new ReporterConfiguration().withLogSpans(true)

则会有如下的日志输出

2021-12-31 14:44:01.734 INFO [game-bus-cache-1][worker-0][LoggingReporter.java:43]29a998394fc0478c869ce7aba3623688 - Span reported: 015dcaea33775a88:015dcaea33775a88:0:1 - UserController.handle(GetAccountList_C2S_Msg)
public class LoggingReporter implements Reporter {
  private final Logger logger;
  public LoggingReporter() {
    this(null);
  }
  public LoggingReporter(Logger logger) {
    if (logger == null) {
      logger = LoggerFactory.getLogger(this.getClass());
    }
    this.logger = logger;
  }
  @Override
  public void report(JaegerSpan span) {
    logger.info("Span reported: {}", span);
  }
  @Override
  public void close() {
    // nothing to do
  }
}

2.读入数据

3.对外端口

Port  Protocol  Component Function
5775  UDP agent accept zipkin.thrift over compact thrift protocol (deprecated, used by legacy clients only)
6831  UDP agent accept jaeger.thrift over compact thrift protocol
6832  UDP agent accept jaeger.thrift over binary thrift protocol
5778  HTTP  agent serve configs
16686 HTTP  query serve frontend
14268 HTTP  collector accept jaeger.thrift directly from clients
14250 HTTP  collector accept model.proto
9411  HTTP  collector Zipkin compatible endpoint (optional)

查看运行中的容器实例:

doker ps

查看所有容器实例,包括没有运行的:

[root@skywalking ~]# docker ps -a
CONTAINER ID        IMAGE                           COMMAND                  CREATED             STATUS              PORTS                                                                                                  NAMES
5181eab3c173        jaegertracing/all-in-one:1.27   "/go/bin/all-in-one-…"   2 hours ago         Up 2 hours          5775/udp, 5778/tcp, 14250/tcp, 6832/udp, 14268/tcp, 0.0.0.0:6831->6831/udp, 0.0.0.0:16686->16686/tcp   silly_bardeen```
root     25723 26830  0 15:48 ?        00:00:00 /usr/bin/docker-proxy -proto tcp -host-ip 0.0.0.0 -host-port 16686 -container-ip 172.17.0.2 -container-port 16686
root     25737 26830  0 15:48 ?        00:00:00 /usr/bin/docker-proxy -proto udp -host-ip 0.0.0.0 -host-port 6831 -container-ip 172.17.0.2 -container-port 6831

你可以通过访问 http://localhost:16686 展示 Jaeger UI.

采样速率

支持设置采样率是 Jaeger 的一个亮点,生产环境系统性能很重要,所以对于所有的请求都开启 Trace 显然会带来比较大的压力,另外,大量的数据也会带来很大存储压力。为了尽量消除分布式追踪采样对系统带来的影响,设置采样率是一个很好的办法。Jaeger官方 支持四种采样类别,分别是 const、probabilistic、rateLimiting 和 remote

const:全量采集,采样率设置0,1 分别对应打开和关闭
probabilistic:概率采集,默认万份之一,取值可在 0 至 1 之间,例如设置为 0.5 的话意为只对 50% 的请求采样
rateLimiting:限速采集,每秒只能采集一定量的数据,如设置2的话,就是每秒采集2个链路数据
remote :是遵循远程设置,取值的含义和 probabilistic 相同,都意为采样的概率,只不过设置为 remote 后,Client 会从 Jaeger Agent 中动态获取采样率设置。
guaranteedThroughput:复合采样,至少每秒采样lowerBound次(rateLimiting),超过lowerBound次的话,按照samplingRate概率来采样(probabilistic),

看了代码,还有两个复合采样器,GuaranteedThroughputSampler 和 PerOperationSampler

总结

总之,Jaeger链路跟踪有很多值得我们学习的地方。

参考链接:

The OpenTracing Semantic Specification

OpenTracing Java Library教程

Opentracing 链路追踪实战

阿里云链路追踪 Tracing Analysis通过 Jaeger 上报 Java 应用数据

Sampling

Jaeger Tracing 采样算法分析

相关实践学习
使用阿里云Elasticsearch体验信息检索加速
通过创建登录阿里云Elasticsearch集群,使用DataWorks将MySQL数据同步至Elasticsearch,体验多条件检索效果,简单展示数据同步和信息检索加速的过程和操作。
ElasticSearch 入门精讲
ElasticSearch是一个开源的、基于Lucene的、分布式、高扩展、高实时的搜索与数据分析引擎。根据DB-Engines的排名显示,Elasticsearch是最受欢迎的企业搜索引擎,其次是Apache Solr(也是基于Lucene)。 ElasticSearch的实现原理主要分为以下几个步骤: 用户将数据提交到Elastic Search 数据库中 通过分词控制器去将对应的语句分词,将其权重和分词结果一并存入数据 当用户搜索数据时候,再根据权重将结果排名、打分 将返回结果呈现给用户 Elasticsearch可以用于搜索各种文档。它提供可扩展的搜索,具有接近实时的搜索,并支持多租户。
目录
相关文章
|
2月前
|
消息中间件 Java 中间件
链路跟踪-SkyWalking系列(三)
链路跟踪-SkyWalking系列(三)
|
2月前
|
监控 Java Shell
链路跟踪-SkyWalking系列(一)
链路跟踪-SkyWalking系列(一)
|
2月前
|
存储 缓存 数据可视化
链路跟踪-SkyWalking系列(二)
链路跟踪-SkyWalking系列(二)
|
6月前
|
Java 微服务
skywalking链路追踪时忽略指定异常
skywalking链路追踪时忽略指定异常
|
存储 机器学习/深度学习 运维
基础篇丨链路追踪(Tracing)其实很简单(3)
基础篇丨链路追踪(Tracing)其实很简单
195 0
基础篇丨链路追踪(Tracing)其实很简单(3)
|
存储 运维 监控
基础篇丨链路追踪(Tracing)其实很简单(2)
基础篇丨链路追踪(Tracing)其实很简单
164 0
基础篇丨链路追踪(Tracing)其实很简单(2)
|
数据采集 调度 数据库
基础篇丨链路追踪(Tracing)其实很简单(1)
基础篇丨链路追踪(Tracing)其实很简单
152 0
|
SQL 缓存 运维
使用篇丨链路追踪(Tracing)很简单:链路实时分析、监控与告警
使用篇丨链路追踪(Tracing)很简单:链路实时分析、监控与告警
6547 10
使用篇丨链路追踪(Tracing)很简单:链路实时分析、监控与告警
|
SQL 缓存 运维
使用篇丨链路追踪(Tracing)很简单:链路拓扑
使用篇丨链路追踪(Tracing)很简单:链路拓扑
31527 7
|
存储 消息中间件 NoSQL
浅谈分布式链路追踪之Jaeger
随着微服务生态的盛行,在基于不同的业务场景中,一个简单的请求往往可能会涉及到多个不同服务类型,此时,若某个服务所提供的业务出现异常,从而可能会导致整个业务处理链路中的问题跟踪、定位及其分析较为困难,服务之间的依赖梳理、组件排查就变得尤为复杂。
1061 0