传统分布式项目有一个痛点,就是如果API比较多,彼此之间的调用关系复杂,遇到了问题很难定位。解决方案是在项目的README或Confluence等文档工具中附上该API与其它API的交互关系、架构图等,然后给每个业务请求生成一个ID,然后整条调用线都会记录下这个id,每个API在打印自己的日志的时候都把这个ID打下来。
这样做非常复杂,你需要在开发每个微服务的时候都要去创建相应的接口和类去处理这个ID,去记录日志。
什么是链路追踪?
而在微服务架构中,这个问题已经有了现成的解决方案,那就是“链路追踪”。链路追踪会收集微服务与微服务之间的调用日志,把它们统计起来,形成一个调用关系图。除了调用关系图以外,它还可以统计请求的比例,请求耗时等等。也就是说,它还可以分析微服务系统在大压力下的可用性和性能
。
Google开源了Dapper链路追踪组件,并在2010年发表了论文《Dapper, a Large-Scale Distributed Systems Tracing Infrastructure》,这篇文章是业内实现链路追踪的标杆和理论基础,具有非常大的参考价值。
目前,链路追踪组件有Google的Dapper,Twitter 的Zipkin,以及阿里的Eagleeye (鹰眼)等,它们都是非常优秀的链路追踪开源组件。
Spring Cloud提供了Spring Cloud Sleuth
这个组件来支持链路追踪的功能。它可以非常方便地与Zipkin集成。本文主要以Zipkin
为例来介绍链路追踪。
实现原理
SpringBoot对Zipkin的自动配置可以使得所有RequestMapping匹配到的endpoints得到监控,以及强化了RestTemplate,对其加了一层拦截器,使得由它发起的HTTP请求也同样被监控。
其实查看源码可以发现,Spring Cloud Sleuth可以支持很多种类型的请求,并对每种请求类型分别做了处理。比如Messaging,Hystrix,Feign,Async等等。详细原理可以参考这篇文章:《Spring Cloud Sleuth消息追踪原理》。
Spring Cloud Sleuth中有这样几个概念:
- Span:一个工作单元,请求到了每个微服务,都会有不同的span。span里面有一个64位的- SpanId,还记录了时间戳等信息。
- Trace:一系列spans组成的一个树状结构。每个Trace有一个TraceID,在一个业务的调用链路中,它是不变的。
- Annotation:记录一个事件。事件有四种:
- cs - Client Sent:客户端发起一个请求,描述这个span的开始;
- sr - Server Received:服务端获得请求并准备开始处理它;
- ss - Server Sent:请求处理的完成(当请求返回客户端);
- cr - Client Received:表明Span的结束,客户端成功接收到服务端的回复。
其中每个Annotation都会记录下自己的时间戳。所以我们可以得到:
- sr - cs = 网络延迟
- ss - sr = 处理时间
- cr - cs = 请求总耗时
这里有一张来自官方文档的原理图:
以及介绍ParentId的图:
如何使用?
- 下载Zipkin,之后启动,启动的命令:
java -jar zipkin-server-2.9.4-exec.jar
启动好的样子如下
之后在浏览器输入localhost:9411
,大概就是这个样子的
- 创建一个项目,因为之前有Feign的例子,所以我这里只是简单的改动一下。
在service-client
,service-feign
指定zipkin server的地址,通过配置spring.zipkin.base-url
指定,在application.yml
里加,完整代码如下:
eureka:
client:
serviceUrl:
defaultZone: http://localhost:8761/eureka/
server:
port: 8765
spring:
application:
name: service-feign
zipkin:
base-url=http://localhost:9411
然后分别启动eureka-server
,service-client
,service-feign
,在浏览器输入http://localhost:8765/sayHello?name=Beck Wang
- 查看效果
在依赖分析这里可以看到依赖关系
当然如果微服务多的话,效果会更好,可以看到微服务之间的调用,用时之类的信息。