简介
- 微服务实施的过程一般会进行细粒度的拆分,服务不允许跨库访问,每个微服务全权负责自己的业务领域。但是很多时候一个功能接口需要多个服务互相配合才能完成,这里自然就会用到服务间的调用和依赖关系。往往这种依赖关系随着业务和系统的演进,即使是资深的架构师也很难清楚的画出一份服务调用关系图来。而Spring Cloud Sleuth为Spring Cloud实现了分布式链路跟踪解决方案。
本文将介绍其springcloudsleuth整合zipkin来作为系统的链路追踪工具,内容有
- 链路追踪调用链信息术语介绍
- 搭建链路追踪服务器
- 微服务集成链路追踪客户端进行调用信息收集
- 链路追踪信息采样频率使用说明
- 集成链路追踪调用链生成插件,生成系统调用链关系图
原文:传送门
注:本文基于springcloud2.1.3 Greenwich.RELEASE 版本
1、链路追踪调用链信息术语介绍
Span
Span是基本的工作单元。Span包括一个64位的唯一ID,一个64位trace码,描述信息,时间戳事件,key-value 注解(tags),span处理者的ID(通常为IP)。
最开始的初始Span称为根span,此span中span id和 trace id值相同。
- 个人对span的解读
span可以分为两类:
- 一类是描述服务内部处理这个接口的过程
- 另一类是描述服务间的一次调用过程
举个栗子,client需要调用service1的一个接口,service1又要调用service2的某个接口。
步骤如下:
- service1接收到client的请求(sr),span1
- service1需要调用service2的某个接口(cs),span2
- service2接收到service1的请求(sr),span2
- service2处理接口逻辑,(内部处理过程) span3
- service2返回接口响应给service1(ss),span2
- service1接收到service2的响应结果(cr),span2
- service1再处理完该接口内的内部逻辑,(内部处理过程) span4
- service1再返回给client(ss)。span1
这里就可以分析出
- span1描述了client–>service1之间的调用过程
- span2描述了service1–>service2之间的调用过程
- span3描述了service2自己的业务逻辑处理过程
- span4描述了service1的内部处理过程
Trance
包含一系列的span,它们组成了一个树型结构。从客户发起请求(request)抵达被追踪系统的边界开始,到被追踪系统向客户返回响应(response)为止的整个过程
Annotation
用于及时记录存在的事件。常用的Annotation如下
- cs - Client Sent:客户端发送一个请求,表示span的开始
- sr - Server Received:服务端接收请求并开始处理它。(sr-cs)等于网络的延迟
- ss - Server Sent:服务端处理请求完成,开始返回结束给服务端。(ss-sr)表示服务端处理请求的时间
- cr - Client Received:客户端完成接受返回结果,此时span结束。(cr-sr)表示客户端接收服务端数据的时间
如果一个服务的调用关系如下:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-x2IOnJ2n-1584002254359)(http://www.danyuanblog.com/file-gateway//ueditor/jsp/upload/image/20191026/1572066157023046128.png)]
那么此时将Span和Trace在一个系统中使用Zipkin注解的过程图形化:每个颜色的表明一个span(总计7个spans,从A到G),每个span有类似的信息
Trace Id = X #调用链ID,用来标识同一个请求 Span Id = D #调用链中的一个步骤,用来描述接口处理的过程 Client Sent #服务间的调用类型的span的调用过程各个阶段的描述,所以只有服务间调用的span才存在此信息
此span表示span的Trance Id是X,Span Id是D,同时它发送一个Client Sent事件
- 该调用链中各个span之间的关系图如下所示:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-K2Hg8rZV-1584002254360)(http://www.danyuanblog.com/file-gateway//ueditor/jsp/upload/image/20191026/1572066197284006549.png)]
调用链中各个span的说明
在Zipkin中展示了上图的跟踪信息,红框里是对上图调用span的跟踪[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6OnfzIJj-1584002254361)(http://www.danyuanblog.com/file-gateway//ueditor/jsp/upload/image/20191026/1572066497886050473.png)]
但是你点击这个trace时,我们只看到4个span
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-WUFzb1OO-1584002254363)(http://www.danyuanblog.com/file-gateway//ueditor/jsp/upload/image/20191026/1572066514925091210.png)]
为什么两个界面显示的span数量不同,一个是7,一个4.
- 1 个 spans:来自servier1的接口http:/start 被调用,分别是Server Received (SR) 和 Server Sent (SS) annotations.
- 2 个 spans:来自 service1 调用service2 的 http:/foo 接口。service1 端有两个span,分别为 Client Sent (CS) 和 Client Received (CR) annotations。service2 端也有两个span,分别为Server Received (SR) 和Server Sent (SS) 。物理上他有2个span,但是从逻辑上说这个他们组成一个RPC调用的span。
- 2个span:来自 service2 调用 service3 的 http:/bar 接口,service2 端有两个span,分别为Client Sent (CS) 和 Client Received (CR) annotations。service3 端也有两个span,分别为Server Received (SR) 和Server Sent (SS) 。物理上他有2个span,但是从逻辑上说它们都是同一个RPC调用的span。
- 2个span:来自 service2 调用 service4 的 http:/bar 接口,service2 端有两个span,分别为Client Sent (CS) 和 Client Received (CR) annotations。service4 端也有两个span,分别为Server Received (SR) and Server Sent (SS) 。物理上他有2个span,但是从逻辑上说这个它们都是同一个RPC调用的span。
所以我们计算物理spans有7个:
- 1个来自 http:/start 被请求
- 2个来自 service1调用service2
- 2个来自 service2调用service3
- 2个来自 service2调用service4.
从逻辑上说,我们只看到4个span:- 1个来自client发起的接口调用
- 3个来自服务之间的接口调用
总结:物理span包含了服务内部处理逻辑span和服务间接口调用过程描述span,逻辑span只记录了服务间接口调用过程描述的span。
2、搭建链路追踪服务器
2.1 简介
链路追踪服务将整合zipkin作为链路追踪日志收集服务器,功能特性说明
- 存储组件为Elasticsearch
- 日志收集方式为RabbitMq异步收集
- 配置信息会用到springcloudconfig
- 注册到Eureka注册中心,启用服务发现功能
2.2 pom.xml引入依赖
<?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>
<groupId>com.danyuanblog.springcloud</groupId>
<artifactId>spring-cloud-zipkin-server</artifactId>
<name>spring-cloud-zipkin-server</name>
<url>http://www.danyuanblog.com</url>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.3.RELEASE</version>
<relativePath /> <!-- lookup parent from repository -->
</parent>
<!-- 定义公共变量 -->
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
<spring-cloud.version>Greenwich.RELEASE</spring-cloud.version>
<zipkin-server.version>2.11.8</zipkin-server.version>
<zipkin-es.version>2.8.4</zipkin-es.version>
</properties>
<dependencies>
<!-- Eureka注册中心客户端依赖 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<!-- 链路追踪相关依赖 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-sleuth</artifactId>
</dependency>
<!-- zipkin服务器依赖 -->
<dependency>
<groupId>io.zipkin.java</groupId>
<artifactId>zipkin-server</artifactId>
<version>${zipkin-server.version}</version>
<exclusions>
<exclusion>
<artifactId>spring-boot-starter-log4j2</artifactId>
<groupId>org.springframework.boot</groupId>
</exclusion>
</exclusions>
</dependency>
<!-- zipkin管理后台依赖 -->
<dependency>
<groupId>io.zipkin.java</groupId>
<artifactId>zipkin-autoconfigure-ui</artifactId>
<version>${zipkin-server.version}</version>
</dependency>
<!-- zipkin 整合Elasticsearch存储组件依赖 -->
<!-- https://mvnrepository.com/artifact/io.zipkin.java/zipkin-autoconfigure-storage-elasticsearch-http -->
<dependency>
<groupId>io.zipkin.java</groupId>
<artifactId>zipkin-autoconfigure-storage-elasticsearch-http</artifactId>
<version>${zipkin-es.version}</version>
</dependency>
<!-- zipkin使用rabbitmq异步收集调用链信息依赖 -->
<dependency>
<groupId>io.zipkin.java</groupId>
<artifactId>zipkin-autoconfigure-collector-rabbitmq</artifactId>
<version>2.11.8</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-stream-rabbit</artifactId>
</dependency>
<!-- 配置中心客户端依赖 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<!-- 连接配置中心重试的依赖 -->
<dependency>
<groupId>org.springframework.retry</groupId>
<artifactId>spring-retry</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-bus-amqp</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<repositories>
<repository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/milestone</url>
</repository>
<repository>
<id>Sonatype</id>
<name>Sonatype Repository</name>
<url>http://repository.sonatype.org/content/groups/public</url>
<layout>default</layout>
<releases>
<enabled>true</enabled>
</releases>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
<repository>
<id>Central</id>
<name>Central Repository</name>
<url>http://repo1.maven.org/maven2</url>
<layout>default</layout>
<releases>
<enabled>true</enabled>
</releases>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
</repositories>
<pluginRepositories>
<pluginRepository>
<name>oss.sonatype.org</name>
<id>oss.sonatype.org</id>
<url>http://oss.sonatype.org/content/groups/public</url>
</pluginRepository>
</pluginRepositories>
</project>
2.3 编辑启动类StartZipkinApplication.java
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import zipkin2.server.internal.EnableZipkinServer;
@EnableZipkinServer
@EnableDiscoveryClient
@SpringBootApplication
public class StartZipkinApplication {
public static void main(String[] args) {
SpringApplication.run(StartZipkinApplication.class, args);
}
}
2.3 配置信息
- 编辑bootstrap.yml
spring:
application:
name: zipkin-server
cloud:
#配置中心客户端信息
config:
discovery:
enabled: true
service-id: config-server
label: master
fail-fast: true
retry:
initial-interval: 2000 #首次重试间隔时间,默认1000毫秒
multiplier: 1.1D #下一次重试间隔时间的乘数,比如开始1000,下一次就是1000*1.1=1100
max-interval: 2000 #最大重试时间,默认2000
max-attempts: 20 #最大重试次数,默认6次
#注册中心客户端信息
eureka:
client:
serviceUrl:
defaultZone: http://localhost:8763/eureka/
instance:
prefer-ip-address: true
leaseRenewalIntervalInSeconds: 10
health-check-url-path: /actuator/health
server:
port: 9411
- 编辑zipkin-server.yml
maxHttpHeaderSize: 8192
spring:
sleuth:
enabled: false
#采样率,推荐0.1,百分之百收集的话存储可能扛不住
sampler:
percentage: 1 #100%采集用于测试
zipkin:
collector:
rabbitmq:
addresses: localhost
port: 5672
username: admin
password: admin
queue: zipkin
storage:
type: elasticsearch #es存储zipkin追踪信息
StorageComponent: elasticsearch
elasticsearch:
hosts: localhost:9200
cluster: elasticsearch
index: zipkin
index-shards: 5
index-replicas: 1
3、微服务集成链路追踪客户端进行调用信息收集
3.1 功能说明
微服务也需要注册到Eureka注册中心,便于使用服务发现功能
- 链路追踪信息收集功能只依赖于zipkin-server服务名,便于zipkin-server做集群化
- 信息收集采用rabbitmq异步收集
3.2 引入maven依赖
<!-- zipkin链路追踪依赖 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-zipkin</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.amqp</groupId>
<artifactId>spring-rabbit</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-stream-rabbit</artifactId>
</dependency>
3.3 配置信息
spring
#链路追踪配置
zipkin:
enabled: true
#base-url: http://zipkin-server/ #采用MQ方式收集便不用配置zipkin服务器信息了
rabbitmq:
queue: zipkin
locator:
discovery:
enabled: true
sender:
type: rabbitmq
sleuth:
sampler:#采样率,推荐0.1,百分之百收集的话存储可能扛不住
probability: 1.0
4、链路追踪信息采样频率使用说明
spring.sleuth.sampler.probability
用于设置链路追踪信息采样率
- 测试环境建议配置为1,表示100%收集
- 线上根据实际用户量来配置,如果接口并发比较高,0.1就足够反应整个平台的服务链路情况了
5、集成链路追踪调用链生成插件,生成系统调用链关系图
- 下载依赖包
curl -sSL https://zipkin.io/quickstart.sh | bash -s io.zipkin.dependencies:zipkin-dependencies:LATEST zipkin-dependencies.jar
- 每天定时生成调用链依赖关系图
#生成当前时间前一天的调用链依赖关系图
STORAGE_TYPE=elasticsearch ES_HOSTS=localhost:9200 ES_INDEX=zipkin java -jar zipkin-dependencies.jar `date -u -d '1 day ago' +%F`
然后进入zipkin-server管理后台,进入Dependencies菜单,选择对应的日期范围
- 生成的调用链关系图是根据接口的实际调用情况绘制而成
- 线条越粗代表调用量越大