Sleuth分布式链路追踪

Sleuth是SpringCloud生态中用于解决分布式链路调用追踪的组件, 微服务架构中要实现链路追踪只需要在客户端和服务端的应用中增加如下依赖即可.

Maven依赖

1
2
3
4
5
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-sleuth</artifactId>
<version>x.x.x.RELEASE</version>
</dependency>

分布式链路示例

这里我们基于之前文章《SpringCloud-Eureka服务注册与发现》中的示例改进

服务端工程

工程结构
1
2
3
4
5
6
7
8
9
10
11
12
13
eureka-provider
├── pom.xml
├── src
│   └── main
│   ├── java
│   │   └── com
│   │   └── cloud
│   │   └── provider
│   │   ├── Application.java
│   │   └── www
│   │   └── SimpleController.java
│   └── resources
│   └── application.properties
Maven依赖
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-sleuth</artifactId>
</dependency>
</dependencies>

<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
服务示例
1
2
3
4
5
6
7
8
9
10
11
12
13
@RestController
@RequestMapping(value="/api")
public class SimpleController {

private final Logger logger = LoggerFactory.getLogger(SimpleController.class);


@GetMapping(value="/select/{id}", produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
public ResponseEntity getUserEntry(@PathVariable("id") long id) {
logger.info("SERVER-PROVIDER:服务端接口实现");
return ResponseEntity.ok("测试数据" + id);
}
}

客户端工程

工程结构
1
2
3
4
5
6
7
8
9
10
11
12
13
14
eureka-consumer-ribbon
├── pom.xml
├── src
│   └── main
│   ├── java
│   │   └── com
│   │   └── cloud
│   │   └── consumer
│   │   └── ribbon
│   │   ├── Application.java
│   │   └── www
│   │   └── RibbonController.java
│   └── resources
│   └── application.properties
Maven依赖
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-ribbon</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-sleuth</artifactId>
</dependency>
</dependencies>

<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
调用示例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@RestController
@RequestMapping(value="/api/invoke")
public class RibbonController {

private static final Logger logger = LoggerFactory.getLogger(RibbonController.class);

@Autowired
private RestTemplate restTemplate;

@GetMapping(value="/select/{id}")
public String remoteInvoke(@PathVariable long id) {
logger.info("SERVER-PROVIDER:服务端接口调用");
return restTemplate.getForObject("http://SERVER-PROVIDER/api/select/{0}", String.class, id);
}
}

调用链追踪

分别启动服务端和客户端,等应用注册到注册中心后进行请求访问

执行调用请求
1
2
[root@localhost ~]$ curl  172.30.12.197:8080/api/invoke/select/2222
测试数据2222
调用链日志

服务端日志

1
2
2018-01-04 18:53:37.832  INFO [server-provider,fbb118782a389c57,24b2c95193a0efc7,false] 9059 --- [nio-7001-exec-3] com.cloud.provider.www.SimpleController  : SERVER-PROVIDER:服务端接口实现
2018-01-04 18:53:40.841 INFO [server-provider,095b60553c871b1e,d9c9dae442a359f0,false] 9059 --- [nio-7001-exec-4] com.cloud.provider.www.SimpleController : SERVER-PROVIDER:服务端接口实现

客户端日志

1
2
2018-01-04 18:53:37.503  INFO [consumer-ribbon,fbb118782a389c57,fbb118782a389c57,false] 9171 --- [nio-8080-exec-1] c.c.c.ribbon.www.RibbonController        : SERVER-PROVIDER:服务端接口调用
2018-01-04 18:53:40.833 INFO [consumer-ribbon,095b60553c871b1e,095b60553c871b1e,false] 9171 --- [nio-8080-exec-2] c.c.c.ribbon.www.RibbonController : SERVER-PROVIDER:服务端接口调用

从控制台输出内容可以看到一些形如的如下的日志信息.这个是分布式服务调动链日志,含义如下:

1
[server-provider,fbb118782a389c57,24b2c95193a0efc7,false]
调用链解读
  • 第一个值: 记录应用名称,也就是application.properties中spring.application.name的值
  • 第二个值: TraceId,用于表示一条请求链路, 一条请求链路有一个唯一的TraceId
  • 第三个值: SpanId,表示一个基本的工作单元,一个请求链路中可能有好几个逻辑调用, 每个逻块为一个基本单元, 所以一个TraceId下会有多个SpanId.
  • 第四个值: false表示是否要将改信息输出到Zipkin/Logstash等服务中来统计或展示. 采集的形式有多种: Logstash、Zipkin、Mq消息、Http等.

分布式链路收集

这里以Zipkin为例展示收集调用链路,Zipkin是Twitter的开源项目,基于Google Dapper实现, 主要用来收集各个服务器上请求链路的跟踪数据,并通过RestApi接口来辅助查询展示.

Zipkin组件介绍

  • Collector: 收集器组件
  • Storage: 存储组件
  • Rest Api: API组件
  • Web UI: UI展示组件

搭建Zipkin服务

工程结构
1
2
3
4
5
6
7
8
9
10
11
12
zipkin-server
├── pom.xml
├── src
│   └── main
│   ├── java
│   │   └── com
│   │   └── cloud
│   │   └── zipkin
│   │   └── server
│   │   └── ZipkinApplication.java
│   └── resources
│   └── application.properties
Maven依赖
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
</dependency>
<dependency>
<groupId>io.zipkin.java</groupId>
<artifactId>zipkin-autoconfigure-ui</artifactId>
</dependency>
<dependency>
<groupId>io.zipkin.java</groupId>
<artifactId>zipkin-server</artifactId>
</dependency>
</dependencies>

<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
主类示例

@EnableZipkinServer开启Zipkin服务

1
2
3
4
5
6
7
8
9
@EnableEurekaClient
@EnableZipkinServer
@SpringBootApplication
public class ZipkinApplication {

public static void main(String[] args) {
SpringApplication.run(ZipkinApplication.class, args);
}
}
配置文件
1
2
3
spring.application.name=zipkin-server
server.port=9411
eureka.client.service-url.defaultZone=http://admin:pwd123@node1.test.com:8001/eureka/,http://admin:pwd123@node1.test.com:8002/eureka/,http://admin:pwd123@node3.test.com:8003/eureka/
效果界面

启动实例、访问172.30.12.197:9411效果如下:

应用配置

服务端配置
Maven依赖
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-sleuth</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-sleuth-zipkin</artifactId>
</dependency>
</dependencies>

<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
配置文件
1
2
3
4
5
6
7
8
9
spring.application.name=server-provider
server.port=7001

spring.http.encoding.charset=utf-8

eureka.client.service-url.defaultZone=http://admin:pwd123@node1.test.com:8001/eureka/,http://admin:pwd123@node1.test.com:8002/eureka/,http://admin:pwd123@node3.test.com:8003/eureka/

spring.sleuth.sampler.percentage=1
spring.zipkin.base-url=http://172.30.12.197:9411/
客户端配置
Maven依赖
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-ribbon</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-sleuth</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-sleuth-zipkin</artifactId>
</dependency>
</dependencies>

<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
配置文件
1
2
3
4
5
6
7
8
9
10
spring.application.name=consumer-ribbon
server.port=8080

eureka.instance.lease-renewal-interval-in-seconds=5
eureka.instance.lease-expiration-duration-in-seconds=10

eureka.client.service-url.defaultZone=http://admin:pwd123@node1.test.com:8001/eureka/,http://admin:pwd123@node1.test.com:8002/eureka/,http://admin:pwd123@node3.test.com:8003/eureka/

spring.sleuth.sampler.percentage=1
spring.zipkin.base-url=http://172.30.12.197:9411/
注册效果

访问测试

服务端日志

1
2018-01-04 20:09:33.547  INFO [server-provider,d9bbb0646c11853d,d0637e9ba0112a6f,true] 10401 --- [nio-7001-exec-3] com.cloud.provider.www.SimpleController  : SERVER-PROVIDER:服务端接口实现

客户端日志

1
2018-01-04 20:09:33.542  INFO [consumer-ribbon,d9bbb0646c11853d,d9bbb0646c11853d,true] 10405 --- [nio-8080-exec-2] c.c.c.ribbon.www.RibbonController        : SERVER-PROVIDER:服务端接口调用

Zipkin查询

链路采集存储

zipkin默认采集数据是存储在内存中的,应用启动后会丢失,所以也可以存储到mysql或elasticsearch. 示例展示存储到Elastic

Maven依赖
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<zipkin.version>2.4.2</zipkin.version>
</properties>

<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>io.zipkin.java</groupId>
<artifactId>zipkin-autoconfigure-ui</artifactId>
<version>${zipkin.version}</version>
</dependency>
<dependency>
<groupId>io.zipkin.java</groupId>
<artifactId>zipkin-server</artifactId>
<version>${zipkin.version}</version>
</dependency>
<dependency>
<groupId>io.zipkin.java</groupId>
<artifactId>zipkin-autoconfigure-storage-elasticsearch-http</artifactId>
<version>${zipkin.version}</version>
</dependency>
</dependencies>

<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
配置文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
spring.application.name=zipkin-server
server.port=9411
eureka.client.service-url.defaultZone=http://admin:pwd123@node1.test.com:8001/eureka/,http://admin:pwd123@node1.test.com:8002/eureka/,http://admin:pwd123@node3.test.com:8003/eureka/

### 当前程序不使用sleuth
spring.sleuth.enabled=false

### 设置elasticsearch存储
zipkin.storage.type=elasticsearch
zipkin.storage.StorageComponent=elasticsearch
zipkin.storage.elasticsearch.cluster=es-test
zipkin.storage.elasticsearch.hosts=172.30.12.197:9200
zipkin.storage.elasticsearch.index=zipkin
zipkin.storage.elasticsearch.index-shards=3
zipkin.storage.elasticsearch.index-replicas=1
zipkin.storage.elasticsearch.max-requests=64