封面图是 凌晨 3点半起来更文的锁屏桌面。
从上周六 7 号到今天的 11 号,我都在医院,小孩因肺炎已经住院了,我白天和晚上的时间需要照顾娃,只能在娃睡觉的时候肝文了。对了,医院没有宽带和 WiFi,我用的手机开的热点~
这篇主要是理论 + 实践相结合。实践部分涉及到如何把链路追踪 Sleuth
+ Zipkin
加到我的 Spring Cloud 《佳必过》开源项目上。
本篇知识点:
微服务
架构其实是一个分布式
的架构,按照业务划分成了多个服务单元。
由于服务单元的数量
是很多的,有可能几千个,而且业务也会更复杂,如果出现了错误和异常,很难去定位。
比如一个请求需要调用多个服务才能完成整个业务闭环,而内部服务的代码逻辑和业务逻辑比较复杂,假如某个服务出现了问题,是难以快速确定那个服务出问题的。
而如果我们加上了分布式链路追踪
,去跟踪一个请求有哪些服务参与其中,参与的顺序是怎样的,这样我们就知道了每个请求的详细经过,即使出了问题也能快速定位。
链路追踪组件有 Twitter 的可视化链路追踪组件 Zipkin
、Google 的 Dapper
、阿里的 Eagleeye
等,而 Sleuth 是 Spring Cloud 的组件。Spring Cloud Sleuth 借鉴了 Dapper 的术语。
本文主要讲解 Sleuth + Zipkin 结合使用来更好地实现链路追踪。
为什么能够进行整条链路的追踪?其实就是一个 Trace ID 将 一连串的 Span 信息连起来了。根据 Span 记录的信息再进行整合就可以获取整条链路的信息。下面
一对一
。
一对多
。
链路追踪系统定义了一些核心注解,用来定义一个请求的开始和结束,注意是微服务之间的请求,而不是浏览器或手机等设备。注解包括:
cs
- Client Sent:客户端发送一个请求,描述了这个请求调用的 Span
的开始时间。注意:这里的客户端指的是微服务的调用者,不是我们理解的浏览器或手机等客户端。sr
- Server Received:服务端获得请求并准备开始处理它,如果将其 sr
减去 cs
时间戳,即可得到网络传输时间。ss
- Server Sent:服务端发送响应,会记录请求处理完成的时间,ss
时间戳减去 sr
时间戳,即可得到服务器请求的时间。cr
- Client Received:客户端接收响应,Span 的结束时间,如果 cr
的时间戳减去 cs
时间戳,即可得到一次微服务调用所消耗的时间,也就是一个 Span
的消耗的总时间。假定三个微服务调用的链路如下图所示:Service 1
调用 Service 2
,Service 2
调用 Service 3
和 Service 4。
那么链路追踪会在每个服务调用的时候加上 Trace ID 和 Span ID。如下图所示:
大白话解释:
Service 1
,生成一个 Request
,Trace ID
和 Span ID
为空,那个时候请求还没有到 Service 1
。
Service 1
,记录了 Trace ID = X,Span ID 等于 A。
Service 1
发送请求给 Service 2
,Span ID 等于 B,被称作 Client Sent,即客户端发送一个请求。
Service 2
,Span ID 等于 B,Trace ID 不会改变,被称作 Server Received,即服务端获得请求并准备开始处理它。
Service 2
开始处理这个请求,处理完之后,Trace ID 不变,Span ID = C。
Service 2
开始发送这个请求给 Service 3
,Trace ID 不变,Span ID = D,被称作 Client Sent,即客户端发送一个请求。
Service 3
接收到这个请求,Span ID = D,被称作 Server Received。
Service 3
开始处理这个请求,处理完之后,Span ID = E。
Service 3
开始发送响应给 Service 2
,Span ID = D,被称作 Server Sent,即服务端发送响应。
Service 3
收到 Service 2
的响应,Span ID = D,被称作 Client Received,即客户端接收响应。
Service 2
开始返回 响应给 Service 1
,Span ID = B,和第三步的 Span ID 相同,被称作 Client Received,即客户端接收响应。
Service 1
处理完响应,Span ID = A,和第二步的 Span ID 相同。
Service 1
开始向客户端返回响应,Span ID = A、
Service 3
向 Service 4 发送请求和 Service 3
类似,对应的 Span ID 是 F 和 G。可以参照上面前面的第六步到第十步。
把以上的相同颜色的步骤简化为下面的链路追踪图:
Service 1
接收到请求。Service 1
发送请求到 Service 2
返回响应给 Service 1
的过程。Service 2
的 中间处理过程。Service 2
发送请求到 Service 3
返回响应给 Service 2
的过程。Service 3
的中间处理过程。Service 3
发送请求到 Service 4 返回响应给 Service 3
的过程。通过 Parent ID 即可找到父节点,整个链路就可以进行跟踪追溯了。
大家可以参照我的 GitHub 开源项目 PassJava(佳必过)。
在 passjava-common 中引入 Spring Cloud 依赖
因为我们使用的链路追踪组件 Sleuth 是 Spring Cloud 的组件,所以我们需要引入 Spring Cloud 依赖。
<dependencyManagement>
<dependencies>
<!-- Spring Cloud 依赖 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Hoxton.SR3</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
引入链路追踪组件 Sleuth 非常简单,在 pom.xml 文件中引入 Sleuth 依赖即可。
在 passjava-common 中引入 Sleuth 依赖:
<!-- 链路追踪组件 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-sleuth</artifactId>
</dependency>
我们先不整合 zipkin 链路追踪可视化组件,而是通过日志的方式来查看链路追踪信息。
文件路径:\PassJava-Platform\passjava-question\src\main\resources\application.properties
添加配置:
logging.level.org.springframework.cloud.openfeign=debug
logging.level.org.springframework.cloud.sleuth=debug
启动以下微服务:
打开 Admin 后台,访问题目中心->题目配置页面,可以看到发送了下面的请求:
http://localhost:8060/api/question/v1/admin/question/list?t=1605170539929&page=1&limit=10&key=
打开控制台,可以看到打印出了追踪日志。
说明:
上面我们通过简单的引入 Sleuth 组件,就可以获取到调用链路,但只能通过控制台的输出信息来看,不太方便。
Zipkin 油然而生,一个图形化的工具。Zipkin 是 Twitter 开源的分布式跟踪系统,主要用来用来收集系统的时序数据,进而可以跟踪系统的调用问题。
而且引入了 Zipkin 组件后,就不需要引入 Sleuth 组件了,因为 Zipkin 组件已经帮我们引入了。
Zipkin 的官文:https://zipkin.io
Zipkin 包含四大组件:
流程解释:
vagrant up
用 Xshell 工具连接 虚拟机。
docker run -d -p 9411:9411 openzipkin/zipkin
访问服务地址:http://192.168.56.10:9411/zipkin。
在公共模块引入 zipkin 依赖
<!-- 链路追踪组件 Zipkin -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-zipkin</artifactId>
</dependency>
因为 zipkin 包里面已经引入了 sleuth 组件,所以可以把之前引入的 sleuth 组件删掉。
在需要追踪的微服务模块下添加 zipkin 配置。
# zipkin 的服务器地址
spring.zipkin.base-url=http://192.168.56.10:9411/
# 关闭服务发现,否则 Spring Cloud 会把 zipkin 的 URL 当作服务名称。
spring.zipkin.discovery-client-enabled=false
# 设置使用 http 的方式传输数据,也可以用 RabbitMQ 或 Kafka。
spring.zipkin.sender.type=web
# 设置采样率为 100 %,默认为 0.1(10%)
spring.sleuth.sampler.probability=1
这里我在 passjava-member 微服务中写了一个 API:
passjava-member 服务的 API:getMemberStudyTimeListTest,访问路径为/studytime/list/test/{id}。
passjava-member 服务远程调用 passjava-study 服务的 API:getMemberStudyTimeListTest。
我用 postman 工具测试 member 服务的 API:
打开 Zipkin 工具,搜索 passjava-member 的链路追踪日志,可以看到有一条记录,相关说明如下图所示:
从图中可以看到 passjava-member 微服务调用了 passjava-study 微服务,如图中左半部分所示。
而且 passjava-study 微服务详细的调用时间都记录得非常清楚,如图中右半部分所示。
时间计算:
还可以用图标的方式查看:
Zipkin 存储数据默认是放在内存中的,如果 Zipkin 重启,那么监控数据也会丢失。如果是生成环境,数据丢失会带来很大问题,所以需要将 Zipkin 的监控数据持久化。而 Zipkin 支持将数据存储到以下数据库:
docker run --env STORAGE_TYPE=elasticsearch --env ES_HOSTS=192.168.56.10:9200 openzipkin/zipkin-dependencies
本篇讲解了链路追踪的核心原理,以及 Sleuth + Zipkin 的组件的原理,以及将这两款组件加到了我的开源项目《佳必过》里面了。
这周真的身心俱疲,娃也是受罪,出院后,娃吃饭也不像以前那么积极了,看到医生那种衣服就怕,连看到照片打印机都怕了。生怕是要给他打针、吃药、做雾化的。还未结婚生娃的抓紧时间学习吧,加油少年~
我是悟空,努力变强,变身超级赛亚人!手写了一套 Spring Cloud 进阶教程和 PMP 刷题小程序。