散布式追踪系统 原理! 一口吻说出

在微服务架构中,一次性恳求往往触及到多个模块,多个两边件,多台机器的相互单干能力成功。

图片来自 Pexels

这一系列调用恳求中,有些是串行的,有些是并行的,那么如何确定这个恳求面前调用了哪些运行,哪些模块,哪些节点及调用的先后顺序?如何定位每个模块的性能疑问?本文将为你揭晓答案。

本文将会从以下几个方面来论述:

散布式追踪系统的原理及作用

如何权衡一个接口的性能好坏,普通咱们至少会关注以下三个目的:

在初期,公司刚起步的时刻,或许多会驳回如下单体架构,关于单体架构咱们该用什么方式来计算以上三个目的呢?

最容易想到的显然是用 AOP:

经常使用 AOP 在调用详细的业务逻辑前后区分打印一下时期即可计算出全体的调用时期,经常使用 AOP 来 catch住意外也可知道是哪里的调用造成的意外。

在单体架构中由于一切的服务,组件都在一台机器上,所以相对来说这些监控目的比拟容易成功。

不过随着业务的极速开展,单体架构肯定会朝微服务架构开展,如下图:

一个稍微复杂的微服务架构

假设有用户反应某个页面很慢,咱们知道这个页面的恳求调用链是A→C→B→D,此时如何定位或许是哪个模块惹起的疑问。

每个服务 Service A,B,C,D 都有好几台机器。怎样知道某个恳求调用了服务的详细哪台机器呢?

可以显著看到,由于无法准确定位每个恳求经过确实切门路,在微服务这种架构下有以下几个痛点:

散布式调用链就是为了处置以上几个疑问而生,它关键的作用如下:

经过散布式追踪系统能很好地定位如下恳求的每条详细恳求链路,从而随便地成功恳求链路追踪,每个模块的性能瓶颈定位与剖析。

知道了散布式调用链的作用,那咱们来看下如何成功散布式调用链的成功及原理。

首先为了处置不同的散布式追踪系统 API 不兼容的疑问,降生了 OpenTracing 规范。

OpenTracing 是一个轻量级的规范化层,它位于运行程序/类库和追踪或日志剖析程序之间。

这样 OpenTracing 经过提供平台有关,厂商有关的 API,使得开发人员能够繁难地减少追踪系统的成功。

说到这大家能否想过 Java 中相似的成功?还记得 JDBC吧,经过提供一套规范的接口让各个厂商去成功,程序员即可面对接口编程,不用关心详细的成功。

这里的接口其实就是规范,所以制订一套规范十分关键,可以成功组件的可插拔。

接上去咱们来看 OpenTracing 的数据模型,关键有以下三个:

了解这三个概念十分关键,为了让大家更好地理解这三个概念,我特地画了一张图:

如图示,一次性下单的完整恳求完整就是一个 Trace,显然关于这个恳求来说,必要求有一个全局标识来标识这一个恳求,每一次性调用就称为一个Span,每一次性调用都要带上全局的 TraceId。

这样才可把全局 TraceId 与每个调用关联起来,这个 TraceId 就是经过 SpanContext传输的,既然要传输显然都要遵照协定来调用。

如图示,咱们把传输协定比作车,把 SpanContext 比作货,把 Span 比作路应该会更好了解一些。

了解了这三个概念,接上去我看看散布式追踪系统如何采集一致图中的微服务调用链。

咱们可以看究竟层有一个 Collector 不时在石破天惊地搜集数据,那么每一次性调用 Collector 会搜集哪些信息呢?

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

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

于是一个完整的散布式追踪系统就成功了。

以上成功看起来确实繁难,但有以下几个疑问要求咱们细心理考一下:

接下我来看看 SkyWalking 是如何处置以上四个疑问的。

SkyWalking 驳回了插件化+javaagent 的方式来成功了 Span 数据的智能采集。

这样可以做到对代码的无侵入性,插件化象征着可插拔,裁减性好(后文会引见如何定义自己的插件)。

咱们知道数据普通分为 Header 和 Body,就像 HTTPhttp 有 Header 和 Body,RocketMQ 也有MessageHeader,Message Body。

Body 普通放着业务数据,所以不宜在 Body 中传递 Context,应该在 Header 中传递 Context,如图示:

Dubbo 中的 Attachment 就相当于 Header,所以咱们把 Context 放在 attachment 中,这样就处置了 Context的传递疑问。

小提示:这里的传递 Context 流程均是在 Dubbo Plugin 处置的,业务无感知,这个 Plugin 是怎样成功的呢,下文会剖析。

要保障全局独一 ,咱们可以驳回散布式或许本地生成的 ID,经常使用散布式话要求有一个发号器,每次恳求都要先恳求一下发号器,会有一次性网络调用的开支。

所以 SkyWalking 最终驳回了本地生成 ID 的方式,它驳回了小名鼎鼎的 Snowflow 算法,性能很高。

Snowflake 算法生成的 id

不过 Snowflake 算法有一个妇孺皆知的疑问:时期回拨,这个疑问或许会造成生成的 id 重复。那么 SkyWalking是如何处置时期回拨疑问的呢。

每生成一个 id,都会记载一下生成 id 的时期(lastTimestamp),假设发现时期比上一次性生成 id的时期(lastTimestamp)还小,那说明出现了时期回拨,此时会生成一个随机数来作为 TraceId。

这里或许就有同窗要较真了,或许会觉得生成的这个随机数也会和已生成的全局 id 重复,能否再加一层校验会好点。

这里要说一下系统设计上的打算取舍疑问了,首先假设针对发生的这个随机数作独一性校验无疑会多一层调用,会有肯定的性能损耗。

但其实时期回拨出现的概率很小(出现之后由于机器时期紊乱,业务会遭到很大影响,所以机器时期的调整肯定要慎之又慎),再加上生成的随机数重合的概率也很小,综合思索这里确实没有必要再加一层全局独一性校验。

关于技术打算的选型,肯定要防止适度设计,过犹不迭。

所有采集会不会影响性能?

恳求这么多,假设对每个恳求调用都采集,那毫无疑问数据量会十分大,但反上来想一下,能否真的有必要对每个恳求都采集呢。

其实没有必要,咱们可以设置采样频率,只采样局部数据,SkyWalking 自动设置了 3 秒采样 3 次,其他恳求不采样,如图示:

这样的采样频率其实足够咱们剖析组件的性能了,按 3 秒采样 3 次这样的频率来采样数据会有啥疑问呢。

理想状况下,每个服务调用都在同一个时期点(如下图示)这样的话每次都在同一时期点采样确实没疑问。

但在消费上,每次服务调用基本无法能都在同一时期点调用,由于时期有网络调用延时等,实践调用状况很或许是下图这样:

这样的话就会造成某些调用在服务 A 上被采样了,在服务 B,C 上不被采样,也就没法剖析调用链的性能,那么 SkyWalking 是如何处置的呢。

它是这样处置的:假设抢先有携带 Context 上来(说明抢先采样了),则下游强迫采集数据。这样可以保障链路完整。

SkyWalking 的基础如下架构,可以说简直一切的的散布式调用都是由以下几个组件组成的:

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

接上去大家必需比拟关心 SkyWalking 的性能,那咱们来看下官网的测评数据:

图中蓝色代表未经常使用 SkyWalking 的体现,橙色代表经常使用了 SkyWalking 的体现,以上是在 TPS 为 5000的状况下测出的数据。

可以看出,不论是 CPU,内存,还是照应时期,经常使用 SkyWalking 带来的性能损耗简直可以疏忽不计。

接上去咱们再来看 SkyWalking 与另一款业界比拟出名的散布式追踪工具 Zipkin,Pinpoint 的对比(在采样率为 1 秒 1 个,线程数500,恳求总数为 5000 的状况下做的对比)。

可以看到在关键的照应时期上 Zipkin(117ms),PinPoint(201ms)远逊色于 SkyWalking(22ms)!

从性能损耗这个目的上看,SkyWalking 完胜!

再看下另一个目的:对代码的侵入性如何,ZipKin 是要求在运行程序中埋点的,对代码的侵入强,而 SkyWalking 驳回javaagent+插件化这种修正字节码的方式可以做到对代码无任何侵入。

除了性能和对代码的侵入性上 SkyWaking 体现不错外,它还有以下好处几个好处:

我司在散布式调用链上的通常

由上文可知 SkyWalking 有很多好处,那么是不是咱们用了它的所有组件了呢,其实不然,来看下其在我司的运行架构:

从图中可以看出咱们只驳回了 SkyWalking 的 Agent来启动采样,丢弃了另外的「数据上报及剖析」,「数据存储」,「数据可视化」三大组件。那为啥不间接驳回 SkyWalking 的整套处置打算呢?

由于在接入 SkyWalking 之前咱们的 Marvin 监控生态体系曾经相对比拟完善了。

假设把其整个交流成 SkyWalking,一来没有必要,Marvin在大少数场景下都能满足咱们的需求,二来系统交流老本高,三来假设从新接入用户学习老本很高。

这也给咱们一个启示:任何产品抢占先机很关键,后续产品的交流老本会很高,抢占先机,也就是抢占了用户的心智,这就像微信只管UI,配置上制造精良,但在国外照样干不过 Whatsapp 一样,由于先机曾经没了。

从另一方面来看,对架构来说,没有最好的,最有最适宜的,联合业务场景去平衡折中才是架构设计的实质。

我司关键作了以下变革和通常:

①预发环境由于调试要求强迫采样

从上文剖析可知 Collector 是在后盾定时采样的,这不挺好的吗,为啥要成功强迫采样呢。

还是为了排查定位疑问,有时线上出现疑问,咱们宿愿在预发上能重现,宿愿能看到这个恳求的完整调用链,所以在预发上成功强迫采样很有必要。

所以咱们对 Skywalking 的 Dubbo 插件启动了变革,成功强迫采样。

咱们在恳求的 Cookie 上带上一个相似 force_flag=true 这样的键值对来示意咱们宿愿强迫采样。

在网关收到这个 Cookie 后,就会在 Dubbo 的 Attachment 里带上force_flag=true 这个键值对。

而后 Skywalking 的 Dubbo 插件就可以据此来判别能否是强迫采样了,假设有这个值即强迫采样,假设没有这个值,则走反常的定时采样。

②成功更细粒度的采样?

哈叫更细粒度的采样。先来看下 Skywalking 自动的采样方式 ,即一致采样。

咱们知道这种方式自动是 3 秒采样前 3 次,其他恳求都摈弃,这样的话有个疑问。

假定在这台机器上在 3 秒内有多个 Dubbo,MySQL,Redis 调用,但在假设前三次都是 Dubbo 调用的话,其他像 MySQL,Redis等调用就采样不到了。

所以咱们对 Skywalking 启动了变革,成功了分组采样,如下:

就是说 3 秒内启动 3 次 Redis,Dubbo,MySQL 等的采样,也就防止了此疑问。

③日志中如何嵌入 TraceId?

输入日志中嵌入 TraceId 便于咱们排查疑问,所以打出出 TraceId 十分有必要,该怎样在日志中嵌入 TraceId 呢?

咱们用的是 log4j,这里就要了解一下 log4j 的插件机制了,log4j准许咱们自定义插件来输入日志的格局,首先咱们要求定义日志的格局,在自定义的日志格局中嵌入 %traceId,作为占位符,如下:

而后咱们再成功一个 log4j 的插件,如下:

首先 log4j 的插件要定义一个类,这个类要承袭 LogEventPatternConverter 这个类,并且用规范 Plugin 将其自身申明为Plugin。

经过 @ConverterKeys 这个注解指定了要交流的占位符,而后在 format 方法里将其交流掉。

这样在日志中就会出现咱们想要的 TraceId,如下:

④我司自研了哪些 Skywalking 插件

SkyWalking 成功了很多插件,不过未提供 Memcached 和 Druid 的插件,所以咱们依据其规范自研了这两者的插件:

插件如何成功呢,可以看到它关键由三个局部组成:

或许大家看了还是疑问,那咱们以 Dubbo Plugin 来繁难解说一下,咱们知道在 Dubbo 服务中,每个恳求从 Netty接纳到信息,递交给业务线程池处置开局,到真正调用到业务方法完结,两边经过了十几个 Filter 的处置:

而 MonitorFilter 可以阻拦一切客户端收回恳求或许服务端处置恳求,所以咱们可以对 MonitorFilter 作增强。

在其调用 Invoke 方法前,将全局 TraceId 注入到其 Invocation 的 Attachment中,这样就可以确保在恳求抵达真正的业务逻辑前就曾经存在全局 TraceId。

所以显然咱们要求在插件中指定咱们要增强的类(MonitorFilter),对其方法(Invoke)做增强,要对这个方法做哪些增强呢?

这就是阻拦器(Inteceptor)要做的事,来看看 Dubbo 插件中的instrumentation(DubboInstrumentation):

咱们再看看下代码中刻画的阻拦器(Inteceptor)干了什么事,以下列出关键步骤:

首先 beforeMethod 代表在口头 MonitorFilter 的 invoke 方法前会调用这里的方法,与之对应的是afterMethod,代表在口头 invoke 方法后作增强逻辑。

其次咱们从第 2,3 点可以看到,不论是 Consumer 还是 Provider, 都对其全局 ID 作了相应处置。

这样确保抵达真正的业务层的时刻保障有了此全局 Traceid,定义好 Instrumentation 和 Interceptor 后,最后一步就是在skywalking.def 里指定定义的类:

这样打包进去的插件就会对 MonitorFilter 的 Invoke 方法启动增强,在 Invoke 方法口头前对期 Attachment 作注入全局TraceId 等操作,这一切都是静默的,对代码无侵入的。

本文由浅入深地引见了散布式追踪系统的原理,置信大家对其作用及上班机制有了比拟深的了解。

特地要求留意的是,引入某项技巧,肯定要联合现有的技术架构作出最正当的选用,就像 SkyWalking 有四个模块,我司只驳回其 Agent采样配置一样,没有最好的技术,只要最适宜的技术。

经过此文,置信大家应该对 SkyWalking 的成功机制有了比拟明晰的意识,文中只是引见了一下 SkyWalking的插件成功方式,不过其毕竟是工业级软件,要了解其博大精湛,还要多读源码哦。

作者:码海

编辑:陶家龙

您可能还会对下面的文章感兴趣: