分布式服务跟踪SpringCloud Sleuth
快速入门
创建 trace-1 9015
pom 依赖
<?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.zk.springcloud</groupId>
<artifactId>springcloud-trace-1</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>springcloud-trace-1</name>
<description>Demo project for Spring Boot</description>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.6.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>Finchley.SR2</spring-cloud.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<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-netflix-ribbon</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>
启动类
package com.zk.springcloud.springcloudtrace1;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
@SpringBootApplication
@RestController
@EnableDiscoveryClient
public class SpringcloudTrace1Application {
@Bean
@LoadBalanced
RestTemplate restTemplate(){
return new RestTemplate();
}
@GetMapping(value = "/trace-1")
public String trace(){
System.out.println("===call trace-1===");
return restTemplate().getForEntity("http://trace-2/trace-2",String.class).getBody();
}
public static void main(String[] args) {
SpringApplication.run(SpringcloudTrace1Application.class, args);
}
}
配置文件
spring.application.name=trace-1
server.port=9015
eureka.client.service-url.defaultZone=http://localhost:9001/eureka/,http://localhost:9004/eureka/
创建 trace-2 9016
pom 依赖同 trace-1 9015
启动类
package com.zk.springcloud.springcloudtrace2;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@SpringBootApplication
@RestController
@EnableDiscoveryClient
public class SpringcloudTrace2Application {
@GetMapping(value = "/trace-2")
public String trace(){
System.out.println("====== <call trace-2> ======");
return "Trace";
}
public static void main(String[] args) {
SpringApplication.run(SpringcloudTrace2Application.class, args);
}
}
配置文件
spring.application.name=trace-2
server.port=9016
eureka.client.service-url.defaultZone=http://localhost:9001/eureka/,http://localhost:9004/eureka/
将 eureka 9001 和 eureka-1 9004 、 trace-1 、 trace-2 都启动
访问 http://localhost:9015/trace-1 返回 Trace
访问 http://localhost:9016/trace-2 返回 Trace
日志打印
trace-1
===call trace-1===
trace-2
====== <call trace-2> ======
实现跟踪
在 trace-1 9015 、trace-2 9016 添加如下依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-sleuth</artifactId>
<version>1.2.5.RELEASE</version>
</dependency>
重启后,调用 访问 http://localhost:9015/trace-1 返回 Trace
控制台打印日志:
2018-10-30 10:12:14.259 INFO [trace-1,2c3ee05efdc3793a,7bf98dfe2ca06882,false] 22288 --- [nio-9015-exec-1] c.n.l.DynamicServerListLoadBalancer : Using serverListUpdater PollingServerListUpdater
跟踪原理
对 trace-2 9016 输出头信息
@GetMapping(value = "/trace-2")
public String trace(HttpServletRequest request){
System.out.println("====== <call trace-2> ====== traceid = " + request.getHeader("X-B3-TraceId")
+ " spanid = "+ request.getHeader("X-B3-SpanId"));
return "Trace";
}
重启后,调用 访问 http://localhost:9015/trace-1 返回 Trace
trace-1
===call trace-1===
2018-10-30 10:40:47.212 INFO [trace-1,79512deb9356ecad,cf672765f1dce864,false] 24248 --- [nio-9015-exec-1] s.c.a.AnnotationConfigApplicationContext : Refreshing SpringClientFactory-trace-2: startup date [Tue Oct 30 10:40:47 CST 2018]; parent: org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext@3224a577
trace-2
====== <call trace-2> ====== traceid = 79512deb9356ecad spanid = cf672765f1dce864
抽样收集
spring.sleuth.sampler.percentage=0.1
Htttp 收集
spring cloud 2.0 入门系列一 (10)分布式链路追踪-Zipkin
从SpringCloud2.0 以后,官方已经不支持自定义服务,官方只提供编译好的jar包供用户使用。
zipkin 搭建
curl -sSL https://zipkin.io/quickstart.sh | bash -s
java -jar zipkin.jar
访问:http://localhost:9411 可以看到 zipkin 界面
为应用引入和配置 Zipkin 服务
trace-1 9015 、 trace-2 9016 添加 zipkin 依赖 和 配置
<!-- 分布式链路追踪 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-zipkin</artifactId>
<version>1.2.5.RELEASE</version>
</dependency>
配置
# zipkin 服务地址
spring.zipkin.base-url=http://localhost:9411
# #样本采集量,默认为0.1,为了测试这里修改为1,正式环境一般使用默认值。
spring.sleuth.sampler.probability=1
调用 http://localhost:9015/trace-1 可在 http://localhost:9411/zipkin 看到请求链路
消息中间件收集
spring boot2.0.0使用zipkin和rabbitmq进行服务链路追踪
修改客户端 trace-1 和 trace-2 添加 pom 依赖
<!-- 消息中间件 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-sleuth-stream</artifactId>
<version>2.0.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-stream-rabbit</artifactId>
<version>2.0.1.RELEASE</version>
</dependency>
配置文件添加 rabbitmq 并将 zipkin 配置注释
# zipkin 服务地址
#spring.zipkin.base-url=http://localhost:9411
spring.rabbitmq.host=localhost
spring.rabbitmq.port=5672
spring.rabbitmq.username=springcloud
spring.rabbitmq.password=123456
使用如下命令启动 zipkin 服务
java -jar zipkin.jar --zipkin.collector.rabbitmq.addresses=localhost --zipkin.collector.rabbitmq.username=springcloud --zipkin.collector.rabbitmq.password=123456
访问 http://localhost:9015/trace-1 再访问 http://localhost:9411/zipkin 可以看到链路追踪
数据存储
默认情况下, Zipkin Server 会将跟踪信息存储在内存中,每次重启 Zipkin Server 都会使之前收集的跟踪信息丢失,并且当有大量跟踪信息时内存存储也会成为瓶颈
创建数据库表 mysql.sql 建表语句
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`);
用如下命令启动 zipkin server ,数据已记录在数据库
java -jar zipkin.jar --zipkin.collector.rabbitmq.addresses=localhost --zipkin.collector.rabbitmq.username=springcloud --zipkin.collector.rabbitmq.password=123456 --zipkin.storage.type=mysql --zipkin.storage.mysql.host=localhost --zipkin.storage.mysql.port=3306 --zipkin.storage.mysql.username=root --zipkin.storage.mysql.password=root --zipkin.storage.mysql.db=zipkin