链路追踪的出现在微服务框架中.
一个大型的系统会被拆分成多个微服务集群.那当一个请求横跨多为服务集群时出现了问题,不便于我们去分析问题的产生在哪个微服务的机器.另外当有多个请求横跨多个微服务集群时,就会产生一个复杂的链路请求网图.所以这时出现了链路追踪技术来帮我们解决该问题.(具体的解决方案,跟我们操作数据库一样,提出一个协议标准,技术厂商只需要提供实现就可以了cuiyaonan2000@163.com)
链路追踪要解决的问题在微服务的大环境下
1、自动采取数据(这个其实是重点,即如何解决不让链路追踪代码侵入微服务代码的情况下去采集微服务工程的请求数据cuiyaonan2000@163.com) 2、分析数据,产生完整调用链:有了请求的完整调用链,问题有很大概率可复现(跨微服务的请求如何保持在一条链路中) 3、数据可视化:每个组件的性能可视化,能帮助我们很好地定位系统的瓶颈,及时找出问题所在(提供可视化的管理以及查看功能)
OpenTracing(链路追踪协议)这个就是实现链路追踪的基础,比如像Spring或者SpringCloud都提供预留的接口,各种链路追踪实现,只需要满足这些接口,就能进行链路追踪实现
OpenTracing 是一个轻量级的标准化层,它位于应用程序/类库和追踪或日志分析程序之间。它的出现是为了解决不同的分布式追踪系统 API 不兼容的问题。
OpenTracing 通过提供与平台和厂商无关的 API,使得开发人员能够方便地添加追踪系统,就像单体架构下的AOP(切面编程)一样。
说到这里,大家是否想过 Java 中类似的实现?还记得 JDBC 吧?JDBC 就是通过提供一套标准的接口让各个厂商去实现,程序员即可面对接口编程,不用关心具体的实现。这里的接口其实就是标准。所以,制定一套标准非常重要,可以实现组件的可插拔。
核心模块OpenTracing 的数据模型,主要有以下三个:
-
- Trace:一个完整请求链路
- Span:一次调用过程(需要有开始时间和结束时间)
- SpanContext:Trace 的全局上下文信息,如里面有traceId
真的是无侵入的动采集么?
其实也是有代码侵入的,只是按照标准的侵入.通过javaagent 以字节码打桩的方式自动生成了代理类(参考cglib生成的代理类)
下图主要看skywalking-agent,它的意思就是在我们启动被监控的微服务的时候只需增加一个参数:
即-javaagent: dubbo-plugin 就可以链路追踪dubbo的微服务,如果要链路追踪redis就增加 -javaagent:redis-plugin 就可以了.多个插件是热拔插的,可以组合存在cuiyaonan2000@163.com
如何跨进程传递 context(或者说跨微服务的链路追踪信息传递)
我们的微服务无非就是http或者dubbo.
不管什么微服务协议他都有类似于Http协议Head的部分.所以一般链路追踪的上下文信息都会放置在Head部分中cuiyaonan2000@163.com
统一链路的请求如何保持在一起(traceId)要保证全局唯一 ,我们可以采用分布式或者本地生成的 ID。使用分布式的话,需要有一个发号器,每次请求都要先请求一下发号器,会有一次网络调用的开销。所以 SkyWalking 最终采用了本地生成 ID 的方式,它采用了大名鼎鼎的 snowflow 算法,性能很高。
请求量多如何避免性能问题
如果对每个请求调用都采集,那毫无疑问数据量会非常大,但反过来想一下,是否真的有必要对每个请求都采集呢?其实没有必要,我们可以设置采样频率,只采样部分数据,SkyWalking 默认设置了 3 秒采样 3 次,其余请求不采样,如图所示:
这样的采样频率其实足够我们分析组件的性能了,按 3 秒采样 3 次,这样的频率来采样数据会有啥问题呢。理想情况下,每个服务调用都在同一个时间点,这样的话每次都在同一时间点采样确实没问题。如下图所示:
但在生产上,每次服务调用基本不可能都在同一时间点调用,因为期间有网络调用延时等,实际调用情况很可能是下图这样:
这样的话就会导致某些调用在服务 A 上被采样了,在服务 B,C 上不被采样,也就没法分析调用链的性能。
那么 SkyWalking 是如何解决的呢?
它是这样解决的:如果上游有携带 Context 过来(说明上游采样了),则下游将强制采集数据,这样可以保证链路完整。