Spring Cloud实战系列(七) - 服务链路追踪Spring Cloud Sleuth

前言

Spring Cloud Sleuth 的主要功能就是为 分布式系统 提供 追踪解决方案,并且兼容支持了 Zipkin,只需要在 pom.xml 文件中引入相应的 依赖 即可。本文主要讲述 服务追踪组件 ZipkinSpring Cloud Sleuth 集成了 Zipkin 组件。它主要用于 聚集 来自各个 异构系统实时监控数据,用来追踪 微服务架构 下的 系统延时问题

正文

1. 相关术语

1.1. Span

Span 是一个基本的 工作单元,用于描述一次 RPC 调用,Span 通过一个 64 位的 spanId 作为 唯一标识Zipkin 中的 Span 还有其他数据信息,比如 摘要时间戳事件关键值注释 (tags) 以及 进度 ID (通常是 IP 地址)。Span 在不断的启动和停止,同时记录了 时间信息,一个 Span 创建后,必须在未来的某个时刻停止它。

1.2. Trace

一系列 Span 组成的一个 树状结构。例如,如果你正在跑一个大型 分布式系统,可能需要创建一个 Trace

1.3. Annotation

表示 基本标注列表,一个 Annotation 可以理解成 Span 生命周期中 重要时刻数据快照,比如一个 Annotation 中一般包含 发生时刻timestamp)、事件类型value)、端点endpoint)等信息。其中 Annotation事件类型 包含以下四类:

  • cs - Client Sent

客户端 发起一个请求,这个 Annotion 描述了这个 Span 的开始。

  • sr - Server Received

服务端 获得请求并 准备开始 处理它,如果将 sr 减去 cs时间戳 便可得到 网络延迟

  • ss - Server Sent

服务端 完成请求处理,如果将 ss 减去 sr时间戳,便可得到 服务端 处理请求消耗的时间。

  • cr - Client Received

客户端 成功接收到 服务端 的响应,如果将 cr 减去 cs时间戳,便可得到 整个请求 所消耗的 总时间

2. 项目结构

本文案例主要由 四个模块 组成:

  • eureka-server:作为 服务注册中心

  • zipkin-server:作为 链路追踪服务中心,负责存储 链路数据

  • service-hi:对外暴露一个 测试接口,同时作为 链路追踪服务端,负责 产生链路数据

  • service-zuul:作为 路由网关,负责 请求转发,同时作为 链路追踪客户端,产生 链路数据,并上传至 zipkin-server

8761 端口 开启 eureka-server 服务注册中心,参考前面的文章即可,这里不再演示创建。

3. 构建zipkin-server

新建一个 Spring Boot 应用模块 zipkin-server,它的 pom.xml 完整依赖如下:

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
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.3.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>

<groupId>io.github.ostenant.springcloud</groupId>
<artifactId>zipkin-server</artifactId>
<version>0.0.1-SNAPSHOT</version>

<name>zipkin-server</name>
<description>Demo project for Spring Boot</description>

<properties>
<java.version>1.8</java.version>
<spring-cloud.version>Dalston.RELEASE</spring-cloud.version>
</properties>

<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
</dependency>
<dependency>
<groupId>io.zipkin.java</groupId>
<artifactId>zipkin-server</artifactId>
</dependency>
<dependency>
<groupId>io.zipkin.java</groupId>
<artifactId>zipkin-autoconfigure-ui</artifactId>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>

<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>

<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>

在应用的入口类上, 加上注解 @EnableZipkinServer,开启 Zipkin Server 的功能。

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

public static void main(String[] args) {
SpringApplication.run(ZipkinServerApplication.class, args);
}
}

配置文件 application.yml 中指定服务端口号为 9411,并向 Eureka 注册中心进行 服务注册

1
2
3
4
5
6
7
8
9
eureka:
client:
service-url:
defaultZone: http://localhost:8761/eureka/
server:
port: 9411
spring:
application:
name: zipkin-server

4. 构建service-hi

新建一个 Spring Boot 应用模块 service-hi,在它的 pom.xml 中引入 引入起步依赖 spring-cloud-starter-zipkin,完整依赖如下:

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
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.3.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>io.github.ostenant.springcloud</groupId>
<artifactId>service-hi</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>eureka-client</name>
<description>Demo project for Spring Boot</description>

<properties>
<java.version>1.8</java.version>
<spring-cloud.version>Dalston.SR1</spring-cloud.version>
</properties>

<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-zipkin</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>

<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>

<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>

在它的 配置文件 application.yml 中通过配置项 spring.zipkin.base-url 指定 zipkin server 的地址。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
eureka:
client:
serviceUrl:
defaultZone: http://localhost:8761/eureka/
server:
port: 8763
spring:
application:
name: service-hi
zipkin:
# base-url: http://localhost:9411/
# 若在同一个注册中心的话可以启用自动发现,省略base-url
locator:
discovery:
enabled: true #自动发现
sleuth:
sampler:
percentage: 1.0

到此为止 ZipKin 客户端 已经整合完毕,最后在 应用启动类 上对外暴露一个 API 接口 方便测试

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@SpringBootApplication
@EnableEurekaClient
@RestController
public class ServiceHiApplication {

public static void main(String[] args) {
SpringApplication.run(ServiceHiApplication.class, args);
}

@Value("${server.port}")
private String port;

@RequestMapping("/hi")
public String home(@RequestParam String name) {
return "Hi " + name + ", I am from port: " + port;
}
}

5. 构建service-zuul

新建一个 Spring Boot 应用模块 service-zuul,在 pom.xml 中引入依赖 spring-cloud-starter-zipkin,完整依赖如下:

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
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.3.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>

<groupId>io.github.ostenant.springcloud</groupId>
<artifactId>service-zuul</artifactId>
<version>0.0.1-SNAPSHOT</version>

<name>service-zuul</name>
<description>Demo project for Spring Boot</description>

<properties>
<java.version>1.8</java.version>
<spring-cloud.version>Dalston.SR1</spring-cloud.version>
</properties>

<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-zuul</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-zipkin</artifactId>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>

<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>

<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>

在它的 配置文件 application.yml 中通过配置项 spring.zipkin.base-url 指定 zipkin server 的地址。同时指定 service-hi 基于 zuul服务路径 匹配前缀。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
server:
port: 8769
spring:
application:
name: service-zuul
client:
service-url:
defaultZone: http://localhost:8761/eureka/
sleuth:
sampler:
percentage: 1.0
zipkin:
# base-url: http://localhost:9411/
# 若在同一个注册中心的话可以启用自动发现,省略base-url
locator:
discovery:
enabled: true #自动发现
zuul:
routes:
api-hi:
path: /api-hi/**
serviceId: service-hi

到这里可以发现,引入 ZipKin 服务只需要 导依赖配置属性 两步即可。在应用的 启动类 上使用 @EnableZuulProxy 注解开启 路由网关

1
2
3
4
5
6
7
8
9
@EnableZuulProxy
@EnableEurekaClient
@SpringBootApplication
public class ServiceZuulApplication {

public static void main(String[] args) {
SpringApplication.run(ServiceZuulApplication.class, args);
}
}

6. 测试追踪过程

完整 链路追踪模块 搭建完毕,总结一下:

  1. 搭建一台 zipkin server 作为 链路服务中心

  2. 给各个 服务 引入 zipkin 依赖,配置 zipkin server 地址即可。

下面按顺序依次启动 eureka-serverservice-zipkinservice-hiservice-zuul。访问服务网关,地址为:
http://localhost:8769/api-hi/hi?name=vainlgory,服务响应内容如下:

Hi Vainlgory, I am from port: 8763

然后访问 http://localhost:9411,即访问 ZipKin 提供的可视化页面。

这个界面用于显示 ZipKin Server 收集的 链路数据,可以根据 服务名开始时间结束时间请求消耗的时间 等条件来查找。单击 Find Tracks按钮,可以查看请求的 调用时间消耗时间,以及请求的 链路情况

单击顶部的 Dependencies 按钮,可以查看服务的 依赖关系

7. 在链路数据中添加自定义数据

现在需要实现一个功能:在 链路数据 中加上请求的 操作人。本案例在 service-zuul 网关服务 中实现。

  1. 新建一个 ZuulFilter 过滤器,它的类型为 post 类型,order900,开启 拦截功能

  2. 在过滤器的 拦截逻辑方法 run() 里面,通过 TraceraddTag() 方法加上 自定义 的数据,在本案例中加上了链路的 操作人

  3. 也可以在这个 过滤器 中获取 当前链路traceld 信息,traceld 作为 链路数据唯一标识,可以存储在 log 日志中,方便后续查找,本案例只是将 traceld 的信息简单地打印在控制台上。代码如下:

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
@Component
public class LoggerFileter extends ZuulFilter {
@Autowired
private Tracer tracer;

@Override
public String filterType() {
return FilterConstants.POST_TYPE;
}

@Override
public int filterOrder() {
return 900;
}

@Override
public boolean shouldFilter() {
return true;
}

@Override
public Object run() {
tracer.addTag("operator","forezp");
System.out.println(tracer.getCurrentSpan().traceIdString());
return null;
}
}

8. 使用RabbitMQ传输链路数据

首先改造 zipkin-server 项目模块,在它的 pom.xml 文件中将 zipkin-server 的依赖去掉,加上
spring-cloud-sleuth-zipkin-streamspring-cloud-starter-stream-rabbit 的依赖,配置如下:

1
2
3
4
5
6
7
8
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-sleuth-zipkin-stream</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-stream-rabbit</artifactId>
</dependency>

zipkin-server 的配置文件 application.yml 中加上 RabbitMQ 的配置,包括 主机名端口用户名密码,代码如下:

1
2
3
4
5
6
spring:
rabbitmq:
host: localhost
port: 5672
username: guest
password: guest

在应用的启动类 ZipkinServerApplication 上把注解 @EnableZipkinServer 替换为注解 @EnableZipkinStreamServer,开启 ZipkinStreamServer,代码如下:

1
2
3
4
5
6
7
8
9
@EnableEurekaClient
@SpringBootApplication
@EnableZipkinStreamServer
public class ZipkinServerApplication {

public static void main(String[] args) {
SpringApplication.run(ZipkinServerApplication.class, args);
}
}

现在来改造 Zipkin Client(包括 service-zuulservice-hi 两个模块),分别在它们的 pom.xml 文件中将 spring-cloud-starter-zipkin 依赖改为 spring-cloud-sleuth-zipkin-streamspring-cloud-starter-stream-rabbit,代码如下:

1
2
3
4
5
6
7
8
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-sleuth-zipkin-stream</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-stream-rabbit</artifactId>
</dependency>

和前面的 zipkin-server 模块一样,同时在配置文件 applicayion.yml 加上 RabbitMQ 的配置。这样,就将链路的 数据上传HTTP 改为用 消息代组件 RabbitMQ 的方式。

9. 在MySQL数据库中存储链路数据

在上面的例子中,Zipkin Server 将数据存储在 内存 中,一旦应用服务 重启,之前的 链路数据全部丢失,这时候就需要引入 持久化机制Zipkin 支持将 链路数据 存储在 MySQLElasticsearchCassandra 数据库中。本节讲解如何使用 MySQL 存储。

Zipkin Client 有两种方式将 链路数据 传输到 Zipkin Server 中,一种是使用 HTTP,另一种是使用 RabbitMQZipkin Server 通过这两种方式来 收集链路数据,并存储在 MySQL 中。

9.1. 使用HTTP传输链路数据

zipkin-server 模块的 pom.xml 文件加上以下依赖:

  • Zipkin Server 的依赖 zipkin-server

  • ZipkinMySQL 存储依赖 zipkin-storage-mysql

  • Zipkin ServerUI 界面依赖 zipkin-autoconfigure-ui

  • MySQL 的连接器依赖 mysql-connector-java

  • JDBC起步依赖 spring-boot-starter-jdbc

代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<dependency>
<groupId>io.zipkin.java</groupId>
<artifactId>zipkin-server</artifactId>
<version>1.19.0</version>
</dependency>
<dependency>
<groupId>io.zipkin.java</groupId>
<artifactId>zipkin-storage-mysql</artifactId>
<version>1.19.0</version>
</dependency>
<dependency>
<groupId>io.zipkin.java</groupId>
<artifactId>zipkin-autoconfigure-ui</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>

zipkin-server 模块的 配置文件 application.yml 中加上 数据源 的配置,包括 数据库Url用户名密码连接驱动,并且需要配置 zipkin.storage.typemysql,代码如下:

1
2
3
4
5
6
7
8
9
spring:
datasource:
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://localhost:3306/spring-cloud-zipkin?useUnicode=true&characterEncoding=utf8&useSSL=false
username: root
password: 123456
zipkin:
storage:
type: mysql

另外需要在 MySQL 数据库中放置数据库的 初始化脚本,创建包括 zipkin_spanszipkin_annotationszipkin_dependencies 这几张表。

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
44
45
46
47
48
CREATE TABLE IF NOT EXISTS zipkin_spans (
`trace_id_high` BIGINT NOT NULL DEFAULT 0 COMMENT 'If non zero, this means the trace uses 128 bit traceIds instead of 64 bit',
`trace_id` BIGINT NOT NULL,
`id` BIGINT NOT NULL,
`name` VARCHAR(255) NOT NULL,
`parent_id` BIGINT,
`debug` BIT(1),
`start_ts` BIGINT COMMENT 'Span.timestamp(): epoch micros used for endTs query and to implement TTL',
`duration` BIGINT COMMENT 'Span.duration(): micros used for minDuration and maxDuration query'
) ENGINE=InnoDB ROW_FORMAT=COMPRESSED CHARACTER SET=utf8 COLLATE utf8_general_ci;

ALTER TABLE zipkin_spans ADD UNIQUE KEY(`trace_id_high`, `trace_id`, `id`) COMMENT 'ignore insert on duplicate';
ALTER TABLE zipkin_spans ADD INDEX(`trace_id_high`, `trace_id`, `id`) COMMENT 'for joining with zipkin_annotations';
ALTER TABLE zipkin_spans ADD INDEX(`trace_id_high`, `trace_id`) COMMENT 'for getTracesByIds';
ALTER TABLE zipkin_spans ADD INDEX(`name`) COMMENT 'for getTraces and getSpanNames';
ALTER TABLE zipkin_spans ADD INDEX(`start_ts`) COMMENT 'for getTraces ordering and range';

CREATE TABLE IF NOT EXISTS zipkin_annotations (
`trace_id_high` BIGINT NOT NULL DEFAULT 0 COMMENT 'If non zero, this means the trace uses 128 bit traceIds instead of 64 bit',
`trace_id` BIGINT NOT NULL COMMENT 'coincides with zipkin_spans.trace_id',
`span_id` BIGINT NOT NULL COMMENT 'coincides with zipkin_spans.id',
`a_key` VARCHAR(255) NOT NULL COMMENT 'BinaryAnnotation.key or Annotation.value if type == -1',
`a_value` BLOB COMMENT 'BinaryAnnotation.value(), which must be smaller than 64KB',
`a_type` INT NOT NULL COMMENT 'BinaryAnnotation.type() or -1 if Annotation',
`a_timestamp` BIGINT COMMENT 'Used to implement TTL; Annotation.timestamp or zipkin_spans.timestamp',
`endpoint_ipv4` INT COMMENT 'Null when Binary/Annotation.endpoint is null',
`endpoint_ipv6` BINARY(16) COMMENT 'Null when Binary/Annotation.endpoint is null, or no IPv6 address',
`endpoint_port` SMALLINT COMMENT 'Null when Binary/Annotation.endpoint is null',
`endpoint_service_name` VARCHAR(255) COMMENT 'Null when Binary/Annotation.endpoint is null'
) ENGINE=InnoDB ROW_FORMAT=COMPRESSED CHARACTER SET=utf8 COLLATE utf8_general_ci;

ALTER TABLE zipkin_annotations ADD UNIQUE KEY(`trace_id_high`, `trace_id`, `span_id`, `a_key`, `a_timestamp`) COMMENT 'Ignore insert on duplicate';
ALTER TABLE zipkin_annotations ADD INDEX(`trace_id_high`, `trace_id`, `span_id`) COMMENT 'for joining with zipkin_spans';
ALTER TABLE zipkin_annotations ADD INDEX(`trace_id_high`, `trace_id`) COMMENT 'for getTraces/ByIds';
ALTER TABLE zipkin_annotations ADD INDEX(`endpoint_service_name`) COMMENT 'for getTraces and getServiceNames';
ALTER TABLE zipkin_annotations ADD INDEX(`a_type`) COMMENT 'for getTraces';
ALTER TABLE zipkin_annotations ADD INDEX(`a_key`) COMMENT 'for getTraces';
ALTER TABLE zipkin_annotations ADD INDEX(`trace_id`, `span_id`, `a_key`) COMMENT 'for dependencies job';

CREATE TABLE IF NOT EXISTS zipkin_dependencies (
`day` DATE NOT NULL,
`parent` VARCHAR(255) NOT NULL,
`child` VARCHAR(255) NOT NULL,
`call_count` BIGINT,
`error_count` BIGINT
) ENGINE=InnoDB ROW_FORMAT=COMPRESSED CHARACTER SET=utf8 COLLATE utf8_general_ci;

ALTER TABLE zipkin_dependencies ADD UNIQUE KEY(`day`, `parent`, `child`);

最后需要在应用的启动类 ZipkinServerApplication 中注入 MySQLStorageBean,代码如下:

1
2
3
4
5
6
7
@Bean
public MySQLStorage mySQLStorage(DataSource datasource) {
return MySQLStorage.builder()
.datasource(datasource)
.executor(Runnable::run)
.build();
}

只需要上述步骤,即可将使用 HTTP 传输的 链路数据 存储在 MySQL 数据库中。

9.2. 使用RabbitMQ传输链路数据

本节的案例是在使用 RabbitMQ 传输数据 基础上进行改造的,只需要改造 zipkin-server 的工程。

zipkin-server 工程的 pom.xml 文件中加上 MySQL连接器依赖 mysql-connector-javaJDBC起步依赖 spring-boot-starter-jdbc,代码如下:

1
2
3
4
5
6
7
8
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>

zipkin-server 模块的 配置文件 application.yml 中加上 数据源 的配置,包括数据库的 Uri用户名密码连接驱动,同样的配置 zipkin.storage.typemysql,代码如下:

1
2
3
4
5
6
7
8
9
spring:
datasource:
url: jdbc:mysql://localhost:3306/spring-cloud-zipkin?useUnicode=true&characterEncoding=utf8&useSSL=false
username: root
password: 123456
driver-class-name: com.mysql.jdbc.Driver
zipkin:
storage:
type: mysql

另外需要在 MySQL数据库中初始化 数据库脚本,具体同上一节。

10. 在ElasticSearch中存储链路数据

并发高 的情况下,使用 MySQL 存储 链路数据 显然不合理,这时可以选择使用 ElasticSearch 作为存储。

下载并安装 ElasticSearchKibana,下载地址为 https://www.elastic.co/products/elasticsearch。安装完成后启动,其中 ElasticSearch默认端口号9200Kibana默认端口号5601

本节是在 第八节 的基础上进行改造。首先在 pom.xml 文件中加上 zipkin 的依赖和 zipkin-autoconfigure-storage-elasticsearch-http 的依赖,代码如下:

1
2
3
4
5
6
7
8
9
10
<dependency>
<groupId>io.zipkin.java</groupId>
<artifactId>zipkin</artifactId>
<version>1.28.1</version>
</dependency>
<dependency>
<groupId>io.zipkin.java</groupId>
<artifactId>zipkin-autoconfigure-storage-elasticsearch-http</artifactId>
<version>1.28.1</version>
</dependency>

在应用的配置文件 application.yml 中加上 Zipkin 的配置,配置了 zipkin存储类型type) 为 Elasticsearch,使用的 存储组件StorageComponent)为 Elasticsearch,然后需要配置 Elasticsearch,包括 hosts( 可以配置 多个实例,用 “,” 隔开)。具体配置代码如下:

1
2
3
4
5
6
7
8
9
10
11
zipkin:
storage:
type: elasticsearch
StorageComponent: elasticsearch
elasticsearch:
cluster: elasticsearch
max-requests: 30
index: zipkin
index-shards: 3
index-replicas: 1
hosts: localhost:9200

只需要完成这些配置,Zipkin Server链路数据 就可以存储在 ElasticSearch 里面。

11. 用Kibana展示链路数据

上一节讲述了如何将 链路数据 存储在 ElasticSearch 中,ElasticSearch 可以和 Kibana 结合,将 链路数据 展示在 Kibana 上。安装完成后启动 KibanaKibana 默认会向 本地端口9200ElasticSearch 读取数据。Kibana默认端口5601,访问 Kibana 的主页 http://localhost:5601

单击 Management 按钮,然后单击 Add New,添加一个 index。上一节 ElasticSearch 中写入 链路数据index 配置的是 zipkin,在 Kibana 界面上填写 zipkin-*,单击 Create 按钮,创建完成 index 后,单击 Discover,页面上出现 链路数据

参考

  • 方志朋《深入理解Spring Cloud与微服务构建》

欢迎关注技术公众号: 零壹技术栈

零壹技术栈

本帐号将持续分享后端技术干货,包括虚拟机基础,多线程编程,高性能框架,异步、缓存和消息中间件,分布式和微服务,架构学习和进阶等学习资料和文章。

文章作者: Chen Vainlgory
文章链接: https://geek.vainlgory.top/2019/01/29/Spring Cloud实战系列(七) - 服务链路追踪Spring Cloud Sleuth/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 零壹技術棧 | VainlgoryのBlog
微信打赏
支付宝打赏