简介
  • 微服务实施的过程一般会进行细粒度的拆分,服务不允许跨库访问,每个微服务全权负责自己的业务领域。但是很多时候一个功能接口需要多个服务互相配合才能完成,这里自然就会用到服务间的调用和依赖关系。往往这种依赖关系随着业务和系统的演进,即使是资深的架构师也很难清楚的画出一份服务调用关系图来。而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的某个接口。

步骤如下:

  1. service1接收到client的请求(sr),span1
  2. service1需要调用service2的某个接口(cs),span2
  3. service2接收到service1的请求(sr),span2
  4. service2处理接口逻辑,(内部处理过程) span3
  5. service2返回接口响应给service1(ss),span2
  6. service1接收到service2的响应结果(cr),span2
  7. service1再处理完该接口内的内部逻辑,(内部处理过程) span4
  8. 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注解的过程图形化:
  • springcloud 链路追踪 哪个更好_分布式

每个颜色的表明一个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菜单,选择对应的日期范围

  • 生成的调用链关系图是根据接口的实际调用情况绘制而成
  • 线条越粗代表调用量越大