「进阶之路」调用链监控原理与zipkin项目简单搭建
在单体服务的架构中,所有的服务,组件都在一台机器上,如果需要监控服务的异常与耗时,往往是比较简单的。我们可以使用 AOP 在调用具体的业务逻辑前后分别打印一下时间即可计算出整体的调用时间。在问题追踪的时候,也可以在关键节点打印日志。
但是在微服务架构里就不同了,一次请求会涉及到多个模块与系统,往往需要多台机器的相互协作才能完成。而一系列的请求,不仅会涉及到串联并联、还有同步异步之分。这个时候,如果依然采取单体架构中服务监控的方式,那么确定这个请求背后调用了哪些服务,哪些模块,哪些节点及调用的先后顺序,调用的耗时,或者追踪调用中出现的问题甚至会让开发人员当场去世。于是乎,我们马上就会想到服务追踪系统。
要实现服务追踪,我们有三点问题需要解决:
- 1、埋点并收集服务调用的上下文数据。
- 2、对收集到的数据进行分析、实时处理。
- 3、数据链路的可视化展示。
通过分布式追踪系统能很好地定位如下请求的每条具体请求链路,从而轻易地实现请求链路追踪:
常用图片
那么,有没有一种规范能让大家都很好地进行链路追踪系统的开发呢?
一、分布式调用链规范OpenTracing
为了解决不同的分布式追踪系统 API 不兼容的问题,诞生了 OpenTracing 规范,OpenTracing 是一个轻量级的标准化层,它位于应用程序/类库和追踪或日志分析程序之间。
1、OpenTracing是什么?
Opentracing是分布式链路追踪的一种规范标准,具有平台无关的特性,开发者只需修改少量的配置代码,就可以在符合Opentracing标准的链路追踪系统之间自由切换。
被跟踪的服务只需要调用Opentracing接口,就可以被任何实现这套接口的跟踪后台(比如Zipkin, Jaeger等等)支持,而作为一个跟踪后台,只要实现了个这套接口,就可以跟踪到任何调用这套接口的服务。同时,Opentracing对进程间跟踪数据传递与追踪的最小独立单位Span进行了标准化。
2、OpenTracing的数据结构
- 1、Span
Span是一条追踪链路中的基本组成要素,也是最小独立单位。一个span表示一个独立的工作单元,比如可以表示一次函数调用,一次http请求等等。
span中包含以下信息:
- 服务名称(调用请求的名字)
- 服务的开始时间和结束时间
- K/V形式的Tags
- K/V形式的截断日志Logs
- SpanContext(span上下文信息,其中包含 Trace ID 和 Span ID)
- References:该span对一个或多个span的引用(通过引用SpanContext来获取)。
- 2、Trace一个完整请求链路,每个调用链由多个 Span 组成。
- 3、SpanContextTrace 的全局上下文信息, 如里面有包含着traceId。
大家可以看我这张图来了解数据结构,一次请求的完整请求完整就是一个Trace, 显然对于这个请求来说,必须要有一个全局标识来标识这一个请求TraceId,每一次调用就称为一个 Span每一次调用都要带上全局的TraceId和自身的SpanId, 这样才可把全局 TraceId 与每个调用关联起来。
3、OpenTracing的实现
了解到OpenTracing的数据结构之后。我们首先要收集服务Tacre的数据。
与单体服务中采用埋点的方式不同,现在主流的zipkin和SkyWalking采用了不同的方法收集服务链路的数据:
- zipkin :拦截请求,发送(HTTP,MQ)数据至zipkin服务,需要的话可以持久化到数据库。
- SkyWalking:使用 agent的形式去接入业务系统,这样就不需要在业务系统里添加任何的日志代码,也能记录到对应的调用数据库。
收集到的数据的内容如下图所示:
如果对每个请求调用都采集,那毫无疑问数据量会非常大。对于已经上线的服务而言,每一次请求的情况都是差不多的(如果出现异常报错当然另说)。其实没有必要对每个请求都采集,我们可以设置采样频率,只采样部分数据,SkyWalking中就默认设置了3秒只采样三次。这样的频率其实足够我们分析性能了(小声哔哔:其实大多数业务根本没有那么多的请求)。同时,为了保证全局一致性,上游服务采样了(SpanContext有说明),那么下游服务一定也需要采样,不然很有可能出现不一致的情况。
得到这些数据以后,对链路数据进行可视化展示就能实现服务追踪了。
二、zipkin实现OpenTracing规范
Zipkin是一款开源的分布式实时数据追踪系统,基于Google Dapper的论文设计而来,由Twitter公司开发贡献,它的架构图如下:
但是光有Zipkin还不够,Spring Cloud为我们提供了Sleuth这个组件,它可以为服务之间调用提供错误补货,耗时分析和链路追踪。
Spring Cloud Sleuth可以结合Zipkin,将信息发送到Zipkin,利用Zipkin的存储来存储信息,利用Zipkin Ui来展示数据(Sleuth 默认采用 Http 方式将 span 传输给 Zipkin)。
1、简单搭建zipkin项目
基于springboot来搭建zipkin还是比较简单的事情,在生成模块的时候已经有了完整的脚手架,只需要在maven(或者gradle等任何仓库)放上对应的依赖就行。
在zipkin项目中引入依赖
<dependencies>
<!--分布式链路追踪-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-sleuth</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-zipkin</artifactId>
</dependency>
<!--RabbitMQ的依赖-->
<dependency>
<groupId>org.springframework.amqp</groupId>
<artifactId>spring-rabbit</artifactId>
</dependency>
<!-- Springboot 相关 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
在service与client项目中也需要引入zipkin依赖
<!-- 被zipkin服务追踪的启动依赖-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-zipkin</artifactId>
</dependency>
为什么选择 RabbitMQ 消息中间件发送 span 信息
sleuth 默认采用 http 通信方式,将数据传给 zipkin 作页面渲染,但是 http 传输过程中如果由于不可抗因素导致 http 通信中断,那么此次通信的数据将会丢失。但是使用RabbitMQ的话,消息队列可以积压千万级别的消息,下次重连之后依然可以可以继续消费。随着线程增多,并发量提升之后,RabbitMQ 异步发送数据明显更具有优势。同时,RabbitMQ 支持消息、队列持久化,可以通过消息状态落库、重回队列、镜像队列等技术手段保证其高可用。具体可以参考RabbitMQ原理。
除了依赖方面,在yaml中,zipkin这样配置
server:
port: 9411
spring:
application:
name: zipkin-server
main:
allow-bean-definition-overriding: true
eureka:
client:
service-url:
defaultZone: http://localhost:8761/eureka/
management:
metrics:
web:
server:
auto-time-requests: false
其它服务端中加入zipkin的配置
spring:
sleuth:
sampler:
probability: 1.0
zipkin:
base-url: http://localhost:9411
在使用 Spring Boot 2.x 版本后,官方就不推荐自行定制编译了,让我们直接使用编译好的 jar 包。所以我们在使用@EnableZipkinServer或@EnableZipkinStreamServer注解的时候要注意搭配好springboot的版本。
@SpringBootApplication
@EnableEurekaClient
@EnableZipkinServer
public class ZipkinServerApplication {
public static void main(String[] args) {
SpringApplication.run(ZipkinServerApplication.class, args);
}
}
2、zipkin UI
搭建完毕以后,我们可以登录 http://localhost:9411/zipkin/ 地址进入zipkin的主页:
点击Find Traces就能看到较为简洁的显示Span的个数以及调用总时延。
我们进入一个完整的调用链后访问其中的一个节点得到以下数据:
然后在Dependencies的选项中能看到调用链路的图,当然,因为测试的原因,并没有建立很复杂的调用链路。
从这一块我们能够感知到,springcloud对zipkin的集成非常友好,不用另外启动什么jar包,可以直接集成在springcloud的环境中,这也是我选择用zipkin作为展示的原因。
三、其他链路追踪系统
Zipkin是Twitter开源的调用链分析工具,目前基于springcloud sleuth得到了广泛的使用,特点是轻量,使用部署简单。
除此之外还有:
- SkyWalking 本土开源的基于字节码注入的调用链分析,以及应用监控分析工具。特点是支持多种插件,UI功能较强,接入端无代码侵入。目前已加入Apache孵化器。
- Pinpoint 韩国人开源的基于字节码注入的调用链分析,以及应用监控分析工具。特点是支持多种插件,UI功能强大,接入端无代码侵入。
- CAT 大众点评开源的基于编码和配置的调用链分析,应用监控分析,日志采集,监控报警等一系列的监控平台工具。
至于开发选型的问题,大家可以根据自己的产品实际选择适合的链路追踪系统,可以参考这篇知乎文章监控系统比较 Skywalking Pinpoint Cat zipkin