Zipkin分布式系统调用链追踪

zipkin-demo

ZIPKIN分布式系统调用链追踪

在公司业务发展过程中,刚开始的时候,我们可能比较关注单个请求的调用耗时情况,调用频次统计等一些基本数据指标,因为这个时候业务比较单一,系统相对来说较为简单清晰,调整和优化起来相对来说比较容易一点。

但是随着系统业务的不断发展,需求的不断增加,整个系统逐渐变得越来越复杂,有可能还会涉及到外部系统以及公司内部其他系统之间的一个交互。这个时候整个系统的调用链将会变得越来复杂(目前大多数都是分布式调用),更多的时候我们的一个前端请求可能最终需要经过多次后端服务的调用才能得到我们想要的结果数据,而在这个过程中,如果调用超时或者调用变得异常的慢或者调用出现异常等等情况,一般情况下我们是无法准确快速得定位出这次请求出现的问题具体是因为那一部分出现了状况引起的。

比如说具体是调用服务A出现了问题,还是服务B不可用亦或者是服务C调用超时等等我们是无法得知的。那这个时候就需要对整个调用链做一次完整的分析才能快速准确的定位出具体的问题,因而分布式调用追踪就诞生了。

什么是分布式系统调用追踪?

  1. 对多个相互协作的子系统之间的调用关系及依赖关系进行追踪记录
  2. 系统与系统之间的调用形式有多种:HTTP、RPC、RMI等等
  3. 通过调用链的方式,将一次请求调用过程完整的串联起来,这样就实现了对请求调用路径的监控

为什么需要进行分布式调用追踪?

  1. 确定服务与服务之间的依赖关系,以便后期优化
  2. 统计请求总耗时,以及各个服务的调用耗时,以便解决系统瓶颈
  3. 当请求变慢或系统出现异常时,需要尽快确定具体是哪个服务出现问题,以便快速排查线上问题

分布式调用追踪需要做什么?

  1. 记录从上游到下游关键节点服务的日志信息,入参、出参、异常堆栈等信息
  2. 关键节点服务的响应耗时
  3. 关键节点服务之间的依赖关系
  4. 将分散的请求串联到一起

几个关键概念

traceId:

标识整个请求链,就是一个全局的跟踪ID,是跟踪的入口点,根据需求来决定在哪生成traceId。比如一个http请求,首先入口是web应用,一般看完整的调用链这里自然是traceId生成的起点,结束点在web请求返回点,因此traceId将在这个调用链中进行传递。

spanId:

标识一次分布式调用,这是下一层的请求跟踪ID,这个也根据自己的需求,比如认为一次rpc,一次sql执行等都可以是一个span。一个traceId包含一个以上的spanId。

parentId:

上一次请求跟踪ID,用来将前后的请求串联起来。

cs:

客户端发起请求的时间,比如dubbo调用端开始执行远程调用之前,标志着Span的开始

cr:

客户端收到处理完请求的时间,标志着Span的结束

ss:

服务端处理完逻辑的时间

sr:

服务端收到调用端请求的时间

客户端调用时间 = cr-cs
服务端处理时间 = sr-ss 
sr-cs:表示网络延迟和时钟抖动
ss-sr:表示服务端处理请求耗时
cr-ss:表示网络延迟和时钟抖动

Zipkin概述

Zipkin官网:https://zipkin.io/

各个业务系统在相互协作调用时,将特定的跟踪消息传递至zipkin,zipkin在收集到跟踪消息之后将其进行聚合处理,存储,展示等,用户可通过web UI方便清晰获得网络延迟、调用链路、系统依赖等。

Zipkin主要涉及到四大组件

  1. Collector收集器:负责接收各service传输的数据
  2. Cassandra存储器:作为Storage的一种,也可以是mysql等,默认存储在内存里面
  3. Query查询器:负责查询Storage中存储的数据信息,并且提供简单的JSON API接口获取数据给WEB UI使用
  4. Web UI展示器:提供简单的web可视化界面

安装

  1. 执行以下命令下载jar包
wget -O zipkin.jar 'https://search.maven.org/remote_content?g=io.zipkin.java&a=zipkin-server&v=LATEST&c=exec'
  1. 由于本身是一个spring boot项目,所以可以直接运行jar
    nohup java -jar zipkin.jar &

  2. 浏览器访问:http://127.0.0.1:9411

简单demo

  1. 新建四个工程项目:service1,service2,service3,service4

  2. 它们之间的依赖关系如下:
    service1调用service2,service2调用service3和service4,service3和service4互相没有依赖,直接返回。

  3. 新建4个springboot项目:

  4. pom.xml文件引入zipkin的相关坐标依赖:

<dependency>
   <groupId>io.zipkin.brave</groupId>
   <artifactId>brave-core</artifactId>
   <version>3.9.0</version>
</dependency>

<dependency>
   <groupId>io.zipkin.brave</groupId>
   <artifactId>brave-spancollector-http</artifactId>
   <version>3.9.0</version>
</dependency>

<dependency>
   <groupId>io.zipkin.brave</groupId>
   <artifactId>brave-web-servlet-filter</artifactId>
   <version>3.9.0</version>
</dependency>

<dependency>
   <groupId>io.zipkin.brave</groupId>
   <artifactId>brave-apache-http-interceptors</artifactId>
   <version>3.9.0</version>
</dependency>

<dependency>
   <groupId>org.apache.httpcomponents</groupId>
   <artifactId>httpclient</artifactId>
</dependency>

  1. 编写zipkin配置类
@Configuration
public class ZipkinConfig {
    /**
     * 配置收集器
     * @return SpanCollector
     */
    @Bean
    public SpanCollector spanCollector(){
        Config config = HttpSpanCollector.Config.builder()
                .compressionEnabled(false)
                .connectTimeout(5000)
                .flushInterval(1)
                .readTimeout(6000)
                .build();
        return HttpSpanCollector.create("http://10.0.4.62:9411", config, new EmptySpanCollectorMetricsHandler());
    }

    /**
     * Brave各工具类的封装
     * @param spanCollector 收集器
     * @return Brave
     */
    @Bean
    public Brave brave(SpanCollector spanCollector){
        //指定ServiceName
        Builder builder = new Builder("Zipkin-Service1");
        builder.spanCollector(spanCollector);
        //设置采集率
        builder.traceSampler(Sampler.create(1));
        return builder.build();
    }

    /**
     * 拦截器,需要serverRequestInterceptor,serverResponseInterceptor 分别完成sr和ss操作
     * @param brave
     * @return
     */
    @Bean
    public BraveServletFilter braveServletFilter(Brave brave){
        return new BraveServletFilter(brave.serverRequestInterceptor(),
                brave.serverResponseInterceptor(), new DefaultSpanNameProvider());
    }

    /**
     * httpClient客户端,需要clientRequestInterceptor,clientResponseInterceptor分别完成cs和cr操作
     * @param brave
     * @return
     */
    @Bean
    public CloseableHttpClient closeableHttpClient(Brave brave){
        CloseableHttpClient httpClient = HttpClients.custom()
                .addInterceptorFirst(new BraveHttpRequestInterceptor(brave.clientRequestInterceptor(),
                        new DefaultSpanNameProvider()))
                .addInterceptorFirst(new BraveHttpResponseInterceptor(brave.clientResponseInterceptor()))
                .build();
        return httpClient;
    }
}

SpanCollector:

配置收集器。

Brave:

各工具类的封装,其中builder.traceSampler(Sampler.ALWAYS_SAMPLE)设置采样比率,0-1之间的百分比。

BraveServletFilter:

作为拦截器,需要serverRequestInterceptor,serverResponseInterceptor 分别完成sr和ss操作

CloseableHttpClient:

添加拦截器,需要clientRequestInterceptor,clientResponseInterceptor 分别完成cs和cr操作,该功能由brave中的brave-okhttp模块提供,同样的道理如果需要记录数据库的延迟只要在数据库操作前后完成cs和cr即可,当然brave提供其封装。

  1. 编写各个项目的controller

注意事项:

  1. service1、service2、service3和service4的服务端口号需要修改成不一样(例如:8081,8082,8083,8084),不然启动会报错。

  2. ZipkinConfig配置类指定的应用服务名称需要改变

测试分布式追踪

  1. 分别启动四个服务。启动完成之后,浏览器访问:http://localhost:8081/service1

  2. 在Web UI界面即可看到调用链以及依赖关系:

Zipkin + Dubbo整合

基本原理:

通过编写filter过滤器,在请求处理的前后发送日志数据,让zipkin生成调用链数据。单独抽离出一个项目,通过注解配置的方式实现调用链的追踪。

  1. 编写自动配置的注解类
  2. 编写自动配置的实现,主要是将特定配置节点的值读取到上下文对象中
  3. 编写调用追踪配置类,主要用于配置追踪的一些参数,zipkin地址
  4. 配置Spring自动加载
a. 在resources/META-INF目录下创建spring.factories文件

b. spring.factories内容为:
org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.touna.loan.trace.config.EnableTraceAutoConfiguration
  1. 追踪上下文
  2. 创建Zipkin日志收集器
使用http的方式将日志信息发送给zipkin服务,中间可以配合压缩等优化手法
  1. 日志收集器代理
使用线程池异步执行日志发送,避免阻塞正常业务逻辑

Dubbo Fillter过滤器

消费端Filter

消费端作为调用链的入口,需要判断是首次调用,还是内部多次调用。如果是首次调用则生成新的traceId和spanId,如果是内部多次调用,那么就直接从TraceContext调用链上下文中获取traceId和spanId。消费端需要通过Invocation的参数列表将生成的traceId和spanId传递到下游系统中。

服务端Filter

基本与消费端的逻辑类似,只是这里将服务端的日志信息发送给zipkin,服务端接收消费端从Invocation的参数列表中传递过来的traceId和spanId,从而将整个RPC的调用逻辑串联起来。

Filter应用

消费端:

1.Application启动类开启自动追踪注解@EnableTraceAutoConfigurationProperties
2.配置消费端过滤器
<dubbo:consumer filter="traceConsumerFilter"/>

服务端:

1.Application启动类启用自动追踪注解@EnableTraceAutoConfigurationProperties
2.配置服务端过滤器
<dubbo:provider filter="traceProviderFilter" />

Demo代码演示:
代码已上传至github:git@github.com:hu1991die/zipkin-demo.git

原文参考:

  1. https://www.cnblogs.com/ASPNET2008/p/6709900.html
  2. https://t.hao0.me/devops/2016/10/15/distributed-invoke-trace.html
  3. https://www.cnblogs.com/binyue/p/5703812.html
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 151,511评论 1 330
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 64,495评论 1 273
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 101,595评论 0 225
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 42,558评论 0 190
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 50,715评论 3 270
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 39,672评论 1 192
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,112评论 2 291
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 29,837评论 0 181
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 33,417评论 0 228
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 29,928评论 2 232
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,316评论 1 242
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 27,773评论 2 234
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,253评论 3 220
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 25,827评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,440评论 0 180
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 34,523评论 2 249
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 34,583评论 2 249

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,048评论 18 139
  • 0 问题背景 随着微服务架构的流行,服务按照不同的维度进行拆分,一次请求往往需要涉及到多个服务。互联网应用构建在不...
    七寸知架构阅读 38,871评论 8 91
  • 2010年谷歌发表了其内部使用的分布式跟踪系统Dapper的论文讲述了Dapper在谷歌内部两年的演变和设计、运维...
    yingyingguigui阅读 1,732评论 0 13
  • 昨日匆匆如梦,今夕浑噩难鸣。 千人万语叹零丁, 只是红尘一命。 总冀青春为伴,尝期旧好同行。 二十虚度看风轻, 不...
    乾隆诗祖阅读 181评论 1 4
  • 好几个星期没有亲自给女儿做饭了,想着婆婆昨天的嘱咐,就把鸡炖了。又炒了女儿爱吃的孜然羊肉和从灵山脚下买回来的千张。...
    焦点周青阅读 142评论 0 1