|
| 1 | +# 使用Spring Cloud Sleuth和Zipkin进行分布式链路跟踪 |
| 2 | + |
| 3 | +## 前言 |
| 4 | + |
| 5 | +我们目前采用的是微服务结构,随着业务发展,服务拆分导致系统调用链路愈发复杂,以TMC服务的机票预订流程为例,创建一个订单需要调用tmc-services、basic-resource、domestic-flight-service、flight-dynamic、insurance-service、mail-service、sms-service等多个服务才能完成,当整个请求变慢或不可用时,我们是无法得知该请求是由某个或某些服务引起的,这时就需要解决如何快速定位服务故障点,以对症下药。于是就有了分布式系统调用跟踪的诞生。 |
| 6 | + |
| 7 | +## 技术选型 |
| 8 | + |
| 9 | +**Spring Cloud Sleuth** + **Zipkin** + **Elasticsearch** |
| 10 | + |
| 11 | +Spring Cloud Sleuth主要功能就是在分布式系统中提供追踪解决方案,并且兼容支持了 Zipkin,我们只需通过添加项目依赖即可实现追踪功能。 |
| 12 | + |
| 13 | +- Spring Cloud Sleuth 数据收集 |
| 14 | +- Elasticsearch 数据存储 |
| 15 | +- Zipkin 数据展示 |
| 16 | + |
| 17 | +## Spring Cloud Sleuth |
| 18 | + |
| 19 | +[Spring Cloud Sleuth](https://cloud.spring.io/spring-cloud-static/spring-cloud-sleuth/2.1.0.RELEASE/single/spring-cloud-sleuth.html)为服务之间调用提供链路追踪。通过Sleuth可以很清楚的了解到一个服务请求经过了哪些服务,每个服务处理花费了多长。从而让我们可以很方便的理清各服务间的调用关系。 |
| 20 | + |
| 21 | +### 术语 |
| 22 | + |
| 23 | +- **Trace**:一系列Span组成的一个树状结构。请求一个服务的API接口,这个API接口,需要调用多个微服务,调用每个微服务都会产生一个新的Span,所有由这个请求产生的Span组成了这个Trace。 |
| 24 | + |
| 25 | +- **Span**: 最基本的工作单元。例如: 发送一个RPC就是一个新的span,同样一次RPC的应答也是。Span通过一个唯一的ID来作为标识,另外,再使用一个ID用于服务调用跟踪。Span也可以带有其他数据,例如:描述,时间戳,键值对标签,起始Span的ID,以及处理ID等等。 Span有起始和结束,它们用于跟踪时间信息。Span应该都是成对出现的,有始必有终,所以一旦创建了一个span,那就必须在未来某个时间点结束它。 |
| 26 | + |
| 27 | +- **Annotation**: 用于记录一个事件的时间信息。一些基础核心的Annotation用于记录请求的起始和结束时间,例如: |
| 28 | + - **cs**: 客户端发送(Client Sent的缩写)。这个annotation表示一个span的起始; |
| 29 | + - **sr**: 服务端接收(Server Received的缩写)。表示服务端接收到请求,并开始处理。如果减去`cs`的时间戳,则可以计算出网络传输耗时。 |
| 30 | + - **ss**: 服务端完成请求处理,应答信息被发回客户端(Server Sent的缩写)。如果减去`sr`的时间戳,则可以计算出服务端处理请求的耗时。 |
| 31 | + - **cr**: 客户端接收(Client Received的缩写)。标志着Span的结束。客户端成功的接收到服务端的应答信息。如果减去`cs`的时间戳,则可以计算出请求的响应耗时。 |
| 32 | + |
| 33 | + 下图,通过可视化的方式描述了Span和Trace的概念: |
| 34 | + |
| 35 | +  |
| 36 | + |
| 37 | +### 支持的组件 |
| 38 | + |
| 39 | +Spring Cloud Sleuth可以追踪以下10种类型的组件 |
| 40 | + |
| 41 | +- Async |
| 42 | +- Scheduled |
| 43 | +- Messaging |
| 44 | +- Hystrix |
| 45 | +- Feign |
| 46 | +- RestTempate |
| 47 | +- Zuul |
| 48 | +- RXJava |
| 49 | +- WebSocket |
| 50 | + |
| 51 | +`以上组件若想实现追踪功能,必须通过注入的方式交由Spring进行管理,否则无法生效。` |
| 52 | + |
| 53 | +## Zipkin |
| 54 | + |
| 55 | +Zipkin是一个开放源代码分布式的跟踪系统,由Twitter公司开源,提供的功能包括:数据的收集、存储、查找和展现。 |
| 56 | + |
| 57 | +每个服务向zipkin报告计时数据,zipkin会根据调用关系通过Zipkin UI生成依赖关系图,显示了每个跟踪请求通过了多少个服务,让开发者可通过一个Web前端轻松的分析数据。 |
| 58 | + |
| 59 | +## 案例实战 |
| 60 | + |
| 61 | +### 构建Zipkin-Server工程 |
| 62 | + |
| 63 | +在程序的启动类Application加上@EnableZipkinServer开启ZipkinServer的功能 |
| 64 | + |
| 65 | +```java |
| 66 | +@SpringBootApplication |
| 67 | +@EnableZipkinServer |
| 68 | +public class Application { |
| 69 | + |
| 70 | + public static void main(String[] args) { |
| 71 | + SpringApplication.run(Application.class, args); |
| 72 | + } |
| 73 | +} |
| 74 | +``` |
| 75 | + |
| 76 | +在配置文件application.yml文件,指定服务名为zipkin-server,端口为xxxx |
| 77 | + |
| 78 | +```java |
| 79 | +server: |
| 80 | + port: xxxx |
| 81 | +spring: |
| 82 | + application: |
| 83 | + name: zipkin-server |
| 84 | +``` |
| 85 | + |
| 86 | +### 需要加入追踪功能的服务(这里以tmc-services为例) |
| 87 | + |
| 88 | +在application_dependencies.gradle中加入sleuth、zipkin的依赖 |
| 89 | + |
| 90 | +```java |
| 91 | +dependencies { |
| 92 | + // for service trace |
| 93 | + compile('org.springframework.cloud:spring-cloud-starter-sleuth:2.1.0.RELEASE') |
| 94 | + // zipkin |
| 95 | + compile('org.springframework.cloud:spring-cloud-starter-zipkin') |
| 96 | +} |
| 97 | +``` |
| 98 | + |
| 99 | +修改application.xml,添加使用sleuth、zipkin的相关配置 |
| 100 | + |
| 101 | +```java |
| 102 | +spring: |
| 103 | + application: |
| 104 | + name: tmc-services |
| 105 | + profiles: |
| 106 | + active: dev |
| 107 | + zipkin: |
| 108 | + base-url: https://dev-zipkin.teyixing.com #设置zipkin-server服务地址 |
| 109 | + sleuth: |
| 110 | + web: |
| 111 | + client: |
| 112 | + enabled: true |
| 113 | + sampler: |
| 114 | + probability: 1.0 # 将采样比例设置为 1.0,也就是全部都需要。默认是 0.1 |
| 115 | +``` |
| 116 | + |
| 117 | +`这里需要注意的是:采样比例设置越高,对系统消耗越大,现在上线初期请求不多,这样设置没有问题,后期应根据实际情况进行适当调整` |
| 118 | + |
| 119 | +修改logback.xml,更改日志记录格式,加入服务名称、traceID、spanID、parentID等信息 |
| 120 | + |
| 121 | +```xml |
| 122 | +<?xml version="1.0" encoding="UTF-8"?> |
| 123 | +<configuration> |
| 124 | + <springProperty scope="context" name="springAppName" source="spring.application.name" /> |
| 125 | + <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"> |
| 126 | + <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder"> |
| 127 | + <pattern>%date [%thread] [serviceId:${springAppName:-}/traceId:%X{traceId}/spanId:%X{spanId}/parentId:%X{parentId}] %-5level %logger{80}.%M - %msg%n</pattern> |
| 128 | + </encoder> |
| 129 | + </appender> |
| 130 | + <appender name="FILE" |
| 131 | + class="ch.qos.logback.core.rolling.RollingFileAppender"> |
| 132 | + <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> |
| 133 | + <FileNamePattern>log/businessDebug.%d{yyyy-MM-dd}.log</FileNamePattern> |
| 134 | + <MaxHistory>30</MaxHistory> |
| 135 | + </rollingPolicy> |
| 136 | + <encoder> |
| 137 | + <pattern>%date [%thread] [serviceId:${springAppName:-}/traceId:%X{traceId}/spanId:%X{spanId}/parentId:%X{parentId}] %-5level %logger{80}.%M - %msg%n</pattern> |
| 138 | + </encoder> |
| 139 | + </appender> |
| 140 | + <root> |
| 141 | + <level value="DEBUG"/> |
| 142 | + <appender-ref ref="FILE"/> |
| 143 | + <appender-ref ref="STDOUT"/> |
| 144 | + </root> |
| 145 | +</configuration> |
| 146 | +``` |
| 147 | + |
| 148 | +项目启动后,调用一个api,可以在控制台看到 |
| 149 | + |
| 150 | +``` log |
| 151 | +2019-05-12 05:19:14,220 [http-nio-60028-exec-9] [serviceId:tmc-services/traceId:8b74d4ed976edf83/spanId:8b74d4ed976edf83/parentId:] INFO c.t.tmc.services.application.service.admin.train.AdminTrainApplicationService.getTrainOrderDetail - Get train order: 96214299298631680 detail by staff: 10060 |
| 152 | +
|
| 153 | +2019-05-12 05:19:14,278 [http-nio-60028-exec-9] [serviceId:tmc-services/traceId:8b74d4ed976edf83/spanId:8b74d4ed976edf83/parentId:] INFO c.t.tmc.services.application.service.admin.train.AdminTrainApplicationService.getTrainOrderDetail - Get train order: 96214299298631680 detail by staff: 10060 successfully |
| 154 | +``` |
| 155 | + |
| 156 | +可以看到日志里已加入服务Id、traceId、spanId、parentId,每个工作单元发送一次请求就会产生一个spanId,每个请求会产生一个tranceId和多个spanId,根据tranceId和spanId就能分析出一个完整的请求都经历了哪些服务单元。 |
| 157 | + |
| 158 | +打开Zipkin查看UI页面 |
| 159 | + |
| 160 | + |
| 161 | + |
| 162 | +能看到请求都经历了哪些服务节点。再点相关连接,可以查看调用顺序,并且还能看到在各个服务节点的处理的时间长度。 |
| 163 | + |
| 164 | + |
0 commit comments