分布式链路追踪技术 Sleuth + Zipkin
分布式链路追踪技术适⽤场景
- 场景描述 为了⽀撑⽇益增⻓的庞⼤业务量,我们会使⽤微服务架构设计我们的系统,使得我们的系统不仅能够通过集群部署抵挡流量的冲击,⼜能根据业务进⾏灵活的扩展。那么,在微服务架构下,⼀次请求少则经过三四次服务调⽤完成,多则跨越⼏⼗个甚⾄是上百个服务节点。那么问题接踵⽽来:
1)如何动态展示服务的调⽤链路?(⽐如A服务调⽤了哪些其他的服务---依赖关系)
2)如何分析服务调⽤链路中的瓶颈节点并对其进⾏调优?(⽐如A—>B—>C,C服务处理时间特别⻓)
3)如何快速进⾏服务链路的故障发现?
- 分布式链路追踪技术 如果我们在⼀个请求的调⽤处理过程中,在各个链路节点都能够记录下⽇志,并最终将⽇志进⾏集中可视化展示,那么我们想监控调⽤链路中的⼀些指标就有希望了⽐如, 请求到达哪个服务实例? 请求被处理的状态怎样? 处理耗时怎样?这些都能够分析出来了...
分布式环境下基于这种想法实现的监控技术就是就是分布式链路追踪(全链路追踪)。
- 市场上的分布式链路追踪⽅案 分布式链路追踪技术已然成熟,产品也不少,国内外都有,⽐如
- Spring Cloud Sleuth + Twitter Zipkin
- 阿⾥巴巴的“鹰眼”
- ⼤众点评的“CAT”
- 美团的“Mtrace”
- 京东的“Hydra”
- 新浪的“Watchman”
- 另外还有最近也被提到很多的Apache Skywalking。
分布式链路追踪技术核⼼思想
本质:记录⽇志,作为⼀个完整的技术,分布式链路追踪也有⾃⼰的理论和概念。微服务架构中,针对请求处理的调⽤链可以展现为⼀棵树,示意如下
上图描述了⼀个常⻅的调⽤场景
- ⼀个请求通过⽹关服务路由到下游的微服务-1
- 然后微服务-1调⽤微服务-2
- 拿到结果后再调⽤微服务-3
- 最后组合微服务-2和微服务-3的结果
- 通过⽹关返回给⽤户
为了追踪整个调⽤链路,肯定需要记录⽇志,⽇志记录是基础,在此之上肯定有⼀些理论概念,当下主流的的分布式链路追踪技术/系统所基于的理念都来⾃于Google的⼀篇论⽂《Dapper, a Large-Scale Distributed Systems TracingInfrastructure》,这⾥⾯涉及到的核⼼理念是什么,我们来看下,还以前⾯的服务调⽤来说
上图标识⼀个请求链路,⼀条链路通过TraceId唯⼀标识,span标识发起的请求信息,各span通过parrentId关联起来
Trace:服务追踪的追踪单元是从客户发起请求(request)抵达被追踪系统的边界 开始,到被追踪系统向客户返回响应(response)为⽌的过程
Trace ID:为了实现请求跟踪,当请求发送到分布式系统的⼊⼝端点时,只需要服务跟踪框架为该请求创建⼀个唯⼀的跟踪标识Trace ID,同时在分布式系统内部流转的时候,框架始终保持该唯⼀标识,直到返回给请求⽅
⼀个Trace由⼀个或者多个Span组成,每⼀个Span都有⼀个SpanId,Span中会记录TraceId,同时还有⼀个叫做ParentId,指向了另外⼀个Span的SpanId,表明⽗⼦关系,其实本质表达了依赖关系
Span ID:为了统计各处理单元的时间延迟,当请求到达各个服务组件时,也是通过⼀个唯⼀标识Span ID来标记它的开始,具体过程以及结束。对每⼀个Span来说,它必须有开始和结束两个节点,通过记录开始Span和结束Span的时间戳,就能统计出该Span的时间延迟,除了时间戳记录之外,它还可以包含⼀些其他元数据,⽐如时间名称、请求信息等。
每⼀个Span都会有⼀个唯⼀跟踪标识 Span ID,若⼲个有序的 span 就组成了⼀个trace。
Span可以认为是⼀个⽇志数据结构,在⼀些特殊的时机点会记录了⼀些⽇志信息,⽐如有时间戳、spanId、TraceId,parentId等,Span中也抽象出了另外⼀个概念,叫做事件,核⼼事件如下:
- CS :client send/start 客户端/消费者发出⼀个请求,描述的是⼀个span开始
- SR : server received/start 服务端/⽣产者接收请求 SR-CS属于请求发送的⽹络延迟
- SS: server send/finish 服务端/⽣产者发送应答 SS-SR属于服务端消耗时间
- CR:client received/finished 客户端/消费者接收应答 CR-SS表示回复需要的时间(响应的⽹络延迟)
Spring Cloud Sleuth (追踪服务框架)可以追踪服务之间的调⽤,Sleuth可以记录⼀个服务请求经过哪些服务、服务处理时⻓等,根据这些,我们能够理清各微服务间的调⽤关系及进⾏问题追踪分析。
- 耗时分析:通过 Sleuth 了解采样请求的耗时,分析服务性能问题(哪些服务调⽤⽐较耗时)
- 链路优化:发现频繁调⽤的服务,针对性优化等Sleuth就是通过记录⽇志的⽅式来记录踪迹数据的
注意:我们往往把Spring Cloud Sleuth 和 Zipkin ⼀起使⽤,把 Sleuth 的数据信息发送给 Zipkin 进⾏聚合,利⽤ Zipkin 存储并展示数据
Sleuth + Zipkin
1)每⼀个需要被追踪踪迹的微服务⼯程都引⼊依赖坐标
<!--链路追踪-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-sleuth</artifactId>
</dependency>
2)每⼀个微服务都修改application.yml配置⽂件,添加⽇志级别
#分布式链路追踪
logging:
level:
org.springframework.web.servlet.DispatcherServlet: debug
org.springframework.cloud.sleuth: debug
至此,我们的sleuth就配置好了,当一个请求到来时,我们在控制台可以观察到 Sleuth 输出的⽇志(全局 TraceId、SpanId等)。
这样的⽇志⾸先不容易阅读观察,另外⽇志分散在各个微服务服务器上,接下来我们使⽤zipkin统⼀聚合轨迹⽇志并进⾏存储展示。
3)结合 Zipkin 展示追踪数据
Zipkin 包括Zipkin Server和 Zipkin Client两部分,Zipkin Server是⼀个单独的服务,Zipkin Client就是具体的微服务
1. 创建 Zipkin Server
pom.xml
<!--zipkin-server的依赖坐标-->
<dependency>
<groupId>io.zipkin.java</groupId>
<artifactId>zipkin-server</artifactId>
<version>2.12.3</version>
<exclusions>
<!--排除掉log4j2的传递依赖,避免和springboot依赖的日志组件冲突-->
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-log4j2</artifactId>
</exclusion>
</exclusions>
</dependency>
<!--zipkin-server ui界面依赖坐标-->
<dependency>
<groupId>io.zipkin.java</groupId>
<artifactId>zipkin-autoconfigure-ui</artifactId>
<version>2.12.3</version>
</dependency>
入口启动类
@SpringBootApplication
@EnableZipkinServer
public class ZipkinServerApplication9411 {
public static void main(String[] args) {
SpringApplication.run(ZipkinServerApplication9411.class,args);
}
// 注入事务控制器
@Bean
public PlatformTransactionManager transactionManager(DataSource dataSource) {
return new DataSourceTransactionManager(dataSource);
}
}
application.yml
server:
port: 9411
management:
metrics:
web:
server:
auto-time-requests: false # 关闭自动检测
2. Zipkin Client 构建(在具体微服务中修改)
pom中添加 zipkin 依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-zipkin</artifactId>
</dependency>
application.yml 中添加对zipkin server的引⽤
3. ZipKin服务解析
使用postman调用请求http://localhost:9002/autodeliver/checkState/1545133
然后打开zipkin网址 http://localhost:9411/zipkin/ 查看相关数据
点击每个请求的详情,进入到详情页面,这里有完整的调用请求
4 追踪数据Zipkin持久化到mysql
我们现在的zipkin服务,里面存放的追踪数据是放在了缓存中的,一旦服务重启,数据就会丢失,为止,需要进行数据持久化
1) 准备数据库脚本
mysql中创建名为zipkin数据库,执行创建表的sql语句:zipkin官方脚本
2)引入pom文件
<!--zipkin针对mysql持久化的依赖-->
<dependency>
<groupId>io.zipkin.java</groupId>
<artifactId>zipkin-autoconfigure-storage-mysql</artifactId>
<version>2.12.3</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.1.10</version>
</dependency>
<!--操作数据库需要事务控制-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
</dependency>
3)修改配置文件,添加数据库相关信息
server:
port: 9411
management:
metrics:
web:
server:
auto-time-requests: false # 关闭自动检测
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/zipkin?useUnicode=true&characterEncoding=utf-8&useSSL=false&allowMultiQueries=true&serverTimezone=GMT%2B8
username: root
password: root
druid:
initialSize: 10
minIdle: 10
maxActive: 30
maxWait: 50000
# 指定zipkin持久化介质为mysql
zipkin:
storage:
type: mysql
4)启动类中注⼊事务管理器
// 注入事务控制器
@Bean
public PlatformTransactionManager transactionManager(DataSource dataSource) {
return new DataSourceTransactionManager(dataSource);
}
至此,zipkin中的数据就持久化到了数据库中。