牛皮!40张图带你看懂分布式追踪系统原理及实践,深入开发就是现在!

分布式调用链标准 - OpenTracing

=============================

知道了分布式调用链的作用,那我们来看下如何实现分布式调用链的实现及原理, 首先为了解决不同的分布式追踪系统 API 不兼容的问题,诞生了 OpenTracing 规范,OpenTracing 是一个轻量级的标准化层,它位于应用程序/类库和追踪或日志分析程序之间。

40张图看懂分布式追踪系统原理及实践

这样 OpenTracing 通过提供平台无关,厂商无关的 API,使得开发人员能够方便地添加追踪系统的实现。

说到这大家是否想过 Java 中类似的实现?还记得 JDBC 吧,通过提供一套标准的接口让各个厂商去实现,程序员即可面对接口编程,不用关心具体的实现。这里的接口其实就是标准,所以制定一套标准非常重要,可以实现组件的可插拔。

40张图看懂分布式追踪系统原理及实践

接下来我们来看 OpenTracing 的数据模型,主要有以下三个

  • Trace:一个完整请求链路

  • Span:一次调用过程(需要有开始时间和结束时间)

  • SpanContext:Trace 的全局上下文信息, 如里面有traceId

理解这三个概念非常重要,为了让大家更好地理解这三个概念,我特意画了一张图

40张图看懂分布式追踪系统原理及实践

如图示,一次下单的完整请求完整就是一个 Trace, 显然对于这个请求来说,必须要有一个全局标识来标识这一个请求,每一次调用就称为一个 Span,每一次调用都要带上全局的 TraceId, 这样才可把全局 TraceId 与每个调用关联起来,这个 TraceId 就是通过 SpanContext 传输的,既然要传输显然都要遵循协议来调用。如图示,我们把传输协议比作车,把 SpanContext 比作货,把 Span 比作路应该会更好理解一些。

理解了这三个概念,接下来我看看分布式追踪系统如何采集统一图中的微服务调用链

40张图看懂分布式追踪系统原理及实践

我们可以看到底层有一个 Collector 一直在默默无闻地收集数据,那么每一次调用 Collector 会收集哪些信息呢。

  1. 全局 trace_id:这是显然的,这样才能把每一个子调用与最初的请求关联起来

  2. span_id: 图中的 0,1,1.1,2,这样就能标识是哪一个调用

  3. parent_span_id:比如 b 调用 d 的 span_id 是 1.1,那么它的 parent_span_id 即为 a 调用 b 的 span_id 即 1,这样才能把两个紧邻的调用关联起来。

有了这些信息,Collector 收集的每次调用的信息如下

40张图看懂分布式追踪系统原理及实践

根据这些图表信息显然可以据此来画出调用链的可视化视图如下

40张图看懂分布式追踪系统原理及实践

于是一个完整的分布式追踪系统就实现了。

以上实现看起来确实简单,但有以下几个问题需要我们仔细思考一下

  1. 怎么自动采集 span 数据:自动采集,对业务代码无侵入

  2. 如何跨进程传递 context

  3. traceId 如何保证全局唯一

  4. 请求量这么多采集会不会影响性能

接下我来看看 SkyWalking 是如何解决以上四个问题的

SkyWalking的原理及架构设计

======================

怎么自动采集 span 数据

==================

SkyWalking 采用了插件化 + javaagent 的形式来实现了 span 数据的自动采集,这样可以做到对代码的 无侵入性,插件化意味着可插拔,扩展性好(后文会介绍如何定义自己的插件)

40张图看懂分布式追踪系统原理及实践

如何跨进程传递 context

======================

我们知道数据一般分为 header 和 body, 就像 http 有 header 和 body, RocketMQ 也有 MessageHeader,Message Body, body 一般放着业务数据,所以不宜在 body 中传递 context,应该在 header 中传递 context,如图示

40张图看懂分布式追踪系统原理及实践

dubbo 中的 attachment 就相当于 header ,所以我们把 context 放在 attachment 中,这样就解决了 context 的传递问题。

40张图看懂分布式追踪系统原理及实践

小提示:这里的传递 context 流程均是在 dubbo plugin 处理的,业务无感知,这个 plugin 是怎么实现的呢,下文会分析

traceId 如何保证全局唯一

====================

要保证全局唯一 ,我们可以采用分布式或者本地生成的 ID,使用分布式话需要有一个发号器,每次请求都要先请求一下发号器,会有一次网络调用的开销,所以 SkyWalking 最终采用了本地生成 ID 的方式,它采用了大名鼎鼎的 snowflow 算法,性能很高。

40张图看懂分布式追踪系统原理及实践

图示: snowflake 算法生成的 id

不过 snowflake 算法有一个众所周知的问题:时间回拨,这个问题可能会导致生成的 id 重复。那么 SkyWalking 是如何解决时间回拨问题的呢。

40张图看懂分布式追踪系统原理及实践

每生成一个 id,都会记录一下生成 id 的时间(lastTimestamp),如果发现当前时间比上一次生成 id 的时间(lastTimestamp)还小,那说明发生了时间回拨,此时会生成一个随机数来作为 traceId。这里可能就有同学要较真了,可能会觉得生成的这个随机数也会和已生成的全局 id 重复,是否再加一层校验会好点。

这里要说一下系统设计上的方案取舍问题了,首先如果针对产生的这个随机数作唯一性校验无疑会多一层调用,会有一定的性能损耗,但其实时间回拨发生的概率很小(发生之后由于机器时间紊乱,业务会受到很大影响,所以机器时间的调整必然要慎之又慎),再加上生成的随机数重合的概率也很小,综合考虑这里确实没有必要再加一层全局唯一性校验。对于技术方案的选型,一定要避免过度设计,过犹不及。

请求量这么多,全部采集会不会影响性能?

=======================

如果对每个请求调用都采集,那毫无疑问数据量会非常大,但反过来想一下,是否真的有必要对每个请求都采集呢,其实没有必要,我们可以设置采样频率,只采样部分数据,SkyWalking 默认设置了 3 秒采样 3 次,其余请求不采样,如图示

40张图看懂分布式追踪系统原理及实践

这样的采样频率其实足够我们分析组件的性能了,按 3 秒采样 3 次这样的频率来采样数据会有啥问题呢。理想情况下,每个服务调用都在同一个时间点(如下图示)这样的话每次都在同一时间点采样确实没问题

40张图看懂分布式追踪系统原理及实践

但在生产上,每次服务调用基本不可能都在同一时间点调用,因为期间有网络调用延时等,实际调用情况很可能是下图这样

40张图看懂分布式追踪系统原理及实践

这样的话就会导致某些调用在服务 A 上被采样了,在服务 B,C 上不被采样,也就没法分析调用链的性能,那么 SkyWalking 是如何解决的呢。

它是这样解决的:如果上游有携带 Context 过来(说明上游采样了),则下游强制采集数据。这样可以保证链路完整。

SkyWalking 的基础架构

====================

SkyWalking 的基础如下架构,可以说几乎所有的的分布式调用都是由以下几个组件组成的

40张图看懂分布式追踪系统原理及实践

首先当然是节点数据的定时采样,采样后将数据定时上报,将其存储到 ES, MySQL 等持久化层,有了数据自然而然可根据数据做可视化分析。

SkyWalking 的性能如何

=======================

接下来大家肯定比较关心 SkyWalking 的性能,那我们来看下官方的测评数据

40张图看懂分布式追踪系统原理及实践

图中蓝色代表未使用 SkyWalking 的表现,橙色代表使用了 SkyWalking 的表现,以上是在 TPS 为 5000 的情况下测出的数据,可以看出,不论是 CPU,内存,还是响应时间,使用 SkyWalking 带来的性能损耗几乎可以忽略不计。

接下来我们再来看 SkyWalking 与另一款业界比较知名的分布式追踪工具 Zipkin, Pinpoint 的对比(在采样率为 1 秒 1 个,线程数 500,请求总数为 5000 的情况下做的对比),可以看到在关键的响应时间上, Zipkin(117ms),PinPoint(201ms)远逊色于 SkyWalking(22ms)!

40张图看懂分布式追踪系统原理及实践

从性能损耗这个指标上看,SkyWalking 完胜!

再看下另一个指标:对代码的侵入性如何,ZipKin 是需要在应用程序中埋点的,对代码的侵入强,而 SkyWalking 采用 javaagent + 插件化这种修改字节码的方式可以做到对代码无任何侵入,除了性能和对代码的侵入性上 SkyWaking 表现不错外,它还有以下优势几个优势

  • 对多语言的支持,组件丰富:目前其支持 Java, .Net Core, PHP, NodeJS, Golang, LUA 语言,组件上也支持dubbo, mysql 等常见组件,大部分能满足我们的需求。

  • 扩展性:对于不满足的插件,我们按照 SkyWalking 的规则手动写一个即可,新实现的插件对代码无入侵。

我司在分布式调用链上的实践

=================

SkyWalking 在我司的应用架构

=======================

由上文可知 SkyWalking 有很多优点,那么是不是我们用了它的全部组件了呢,其实不然,来看下其在我司的应用架构

40张图看懂分布式追踪系统原理及实践

从图中可以看出我们只采用了 SkyWalking 的 agent 来进行采样,放弃了另外的「数据上报及分析」,「数据存储」,「数据可视化」三大组件,那为啥不直接采用 SkyWalking 的整套解决方案呢,因为在接入 SkyWalking 之前我们的 Marvin 监控生态体系已经相对比较完善了,如果把其整个替换成 SkyWalking,一来没有必要,Marvin 在大多数场景下都能满足我们的需求,二来系统替换成本高,三来如果重新接入用户学习成本很高。

这也给我们一个启示:任何产品抢占先机很重要,后续产品的替换成本会很高,抢占先机,也就是抢占了用户的心智,这就像微信虽然 UI,功能上制作精良,但在国外照样干不过 Whatsapp 一样,因为先机已经没了。

从另一方面来看,对架构来说,没有最好的,只有最合适的,结合当前业务场景去平衡折中才是架构设计的本质

我司对 SkyWalking 作了哪些改造和实践

============================

我司主要作了以下改造和实践

  1. 预发环境由于调试需要强制采样

  2. 实现更细粒度的采样?

  3. 日志中嵌入traceId

  4. 自研实现了 SkyWalking 插件

预发环境由于调试需要强制采样

==================

从上文分析可知 Collector 是在后台定时采样的,这不挺好的吗,为啥要实现强制采样呢。还是为了排查定位问题,有时线上出现问题,我们希望在预发上能重现,希望能看到这个请求的完整调用链,所以在预发上实现强制采样很有必要。所以我们对 Skywalking 的 dubbo 插件进行了改造,实现强制采样

我们在请求的 Cookie 上带上一个类似 force_flag = true 这样的键值对来表示我们希望强制采样,在网关收到这个 Cookie 后,就会在 dubbo 的 attachment 里带上force_flag = true 这个键值对,然后 skywalking 的 dubbo 插件就可以据此来判断是否是强制采样了,如果有这个值即强制采样,如果没有这个值,则走正常的定时采样。

40张图看懂分布式追踪系统原理及实践

实现更细粒度的采样?

==============

哈叫更细粒度的采样。先来看下 skywalking 默认的采样方式 ,即统一采样

40张图看懂分布式追踪系统原理及实践

我们知道这种方式默认是 3 秒采样前 3 次,其他请求都丢弃,这样的话有个问题,假设在这台机器上在 3 秒内有多个 dubbo,mysql,redis 调用,但在如果前三次都是 dubbo 调用的话,其他像 mysql, redis 等调用就采样不到了,所以我们对 skywalking 进行了改造,实现了分组采样,如下

40张图看懂分布式追踪系统原理及实践

就是说 3 秒内进行 3 次 redis, dubbo, mysql 等的采样,也就避免了此问题

日志中如何嵌入traceId?

===================

输出日志中嵌入 traceId 便于我们排查问题,所以打出出 traceId 非常有必要,该怎么在日志中嵌入 traceId 呢?我们用的是 log4j,这里就要了解一下 log4j 的插件机制了,log4j 允许我们自定义插件来输出日志的格式,首先我们需要定义日志的格式,在自定义的日志格式中嵌入 %traceId, 作为占位符,如下

40张图看懂分布式追踪系统原理及实践

然后我们再实现一个 log4j 的插件,如下

40张图看懂分布式追踪系统原理及实践

首先 log4j 的插件要定义一个类,这个类要继承 LogEventPatternConverter 这个类,并且用标准 Plugin 将其自身声明为 Plugin,通过 @ConverterKeys 这个注解指定了要替换的占位符,然后在 format 方法里将其替换掉。这样在日志中就会出现我们想要的 TraceId ,如下

40张图看懂分布式追踪系统原理及实践

我司自研了哪些 skywalking 插件

=========================

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数Java工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注Java获取)

img

最后

终极手撕架构师的学习笔记:分布式+微服务+开源框架+性能优化

image

《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注Java获取)

img

最后

终极手撕架构师的学习笔记:分布式+微服务+开源框架+性能优化

[外链图片转存中…(img-Imeudl2N-1713754686341)]

《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!

  • 17
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值