1. 簡介
Spring Cloud Sleuth為Spring Cloud實作了分布式跟蹤解決方案。
1.1 基本術語
Spring Cloud Sleuth借鑒了Dapper的術語。
Span:基本工作單元,例如,在一個建立的span中發送一個RPC等同于發送一個回應請求給RPC,span通過一個64位ID唯一辨別,trace以另一個64位ID表示,span還有其他資料資訊,比如摘要、時間戳事件、關鍵值注釋(tags)、span的ID、以及進度ID(通常是IP位址)
Spans在不斷的啟動和停止,同時記錄了時間資訊,當你建立了一個span,你必須在未來的某個時刻停止它。
Trace:一系列spans組成的一個樹狀結構,例如,如果你正在跑一個分布式大資料工程,你可能需要建立一個trace。
Annotation:用來及時記錄一個事件的存在,一些核心annotations用來定義一個請求的開始和結束
-
- Client Sent -用戶端發起一個請求,這個annotion描述了這個span的開始cs
-
- Server Received -服務端獲得請求并準備開始處理它,如果将其sr
減去sr
時間戳便可得到網絡延遲cs
-
- Server Sent -注解表明請求處理的完成(當請求傳回用戶端),如果ss
減去ss
時間戳便可得到服務端需要的處理請求時間sr
-
- Client Received -表明span的結束,用戶端成功接收到服務端的回複,如果cr
cr
減去cs時間戳便可得到用戶端從服務端擷取回複的所有所需時間
下圖顯示了Span和Trace在一個系統中使用Zipkin注解的過程圖形化:
每種顔色表示一個span(有七個span-從A到G)。請考慮以下注意事項:
Trace Id = X
Span Id = D
Client Sent
該說明指出,目前span跟蹤編号設定為X和span辨別設定為d。此外,
Client Sent
事件發生了。
下圖顯示了跨度的父子關系
Zipkin 和 Config 結構類似,分為服務端 Server,用戶端 Client,用戶端就是各個微服務應用。
2. 搭建 Zipkin 服務端
在 Spring Boot 2.0 版本之後,官方已不推薦自己搭建定制了,而是直接提供了編譯好的 jar 包。詳情可以檢視官網:https://zipkin.io/pages/quickstart.html
本文介紹的還是自己搭建zipkin-server,原因本人項目上是使用的spring cloud 體系,想結合eureka注冊中心統一展示管理
2.1 建立 zipkin-server
引入依賴。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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.example</groupId>
<artifactId>spring-cloud-sleuth-zipkin-demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<artifactId>server-zipkin</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>server-zipkin</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<!--排除這個slf4j-log4j12-->
<exclusions>
<exclusion>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
<!--引入的zipkinServer依賴-->
<!-- zipkin 服務類-->
<dependency>
<groupId>io.zipkin.java</groupId>
<artifactId>zipkin-server</artifactId>
<version>2.12.2</version>
</dependency>
<!-- zipkin 界面-->
<dependency>
<groupId>io.zipkin.java</groupId>
<artifactId>zipkin-autoconfigure-ui</artifactId>
<version>2.12.2</version>
</dependency>
<!-- 使用消息的方式收集資料(使用rabbitmq) -->
<dependency>
<groupId>io.zipkin.java</groupId>
<artifactId>zipkin-autoconfigure-collector-rabbitmq</artifactId>
<version>2.12.2</version>
</dependency>
<!-- zipkin 存儲到資料庫需要引入以下3個依賴 -->
<dependency>
<groupId>io.zipkin.java</groupId>
<artifactId>zipkin-autoconfigure-storage-mysql</artifactId>
<version>2.12.2</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
<exclusions>
<exclusion>
<artifactId>logback-classic</artifactId>
<groupId>ch.qos.logback</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.16</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
2.2 配置檔案:
#配置服務及端口
server:
port: 9411 #官方用的是9411,咱們也用這個
spring:
main:
allow-bean-definition-overriding: false #zipkin啟動報錯 解決The bean 'characterEncodingFilter', defined in class path resource [zipkin/autoconfigure/ui/ZipkinUiAutoConfiguration.class], could not be registered. A bean with that name has already been defined in class path resource [org/springframework/boot/autoconfigure/web/servlet/HttpEncodingAutoConfiguration.class] and overriding is disabled.Action:
application:
name: server-zipkin
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/zipkin?characterEncoding=utf8&useSSL=true&verifyServerCertificate=false
username: root
password: 123456
management:
metrics:
web:
server:
auto-time-requests: false #zipkin啟動報錯 解決,Prometheus requires that all meters with the same name have the same set of tag keys. There is already an existing meter named 'http_server_requests_seconds' containing tag keys [exception, method, outcome, status, uri]. The meter you are attempting to register has keys [method, status, uri].
zipkin:
collector:
rabbitmq:
addresses: 192.168.41.16:5672
password: guest
username: guest
virtual-host: /
queue: zipkin
storage:
type: mysql
2.3 啟動類:
@EnableZipkinServer
@SpringBootApplication
public class ServerZipkinApplication {
public static void main(String[] args) {
SpringApplication.run(ServerZipkinApplication.class, args);
}
@Bean
@Primary
public MySQLStorage mySQLStorage(DataSource datasource) {
return MySQLStorage.newBuilder().datasource(datasource).executor(Runnable::run).build();
}
}
這裡注入一個DataSource,需要在資料庫建幾張表
SQL語句參見:https://github.com/openzipkin/zipkin/blob/master/zipkin-storage/mysql-v1/src/main/resources/mysql.sql
任一方式啟動後,通路 http://localhost:9411,可以看到服務端已經搭建成功
3 搭建 Zipkin 用戶端
建立兩個服務,service-hello、service-hi,service-hello 實作一個 REST 接口 /hello,/hello/hi,該接口裡調用/helloe/hi調用 service-hi 應用的接口。
3.1 建立 service-hello
3.2 引入依賴,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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.example</groupId>
<artifactId>spring-cloud-sleuth-zipkin-demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>service-hello</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>service-hello</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<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>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-zipkin</artifactId>
<version>2.1.1.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
3.3 配置檔案:
server:
port: 8482
spring:
application:
name: service-hello
rabbitmq:
host: 192.168.41.16
port: 5672
username: guest
password: guest
zipkin:
rabbitmq:
queue: zipkin
sleuth:
sampler:
#采樣率,推薦0.1,百分之百收集的話存儲可能扛不住
probability: 1.0 #Sleuth 預設采樣算法的實作是 Reservoir sampling,具體的實作類是 PercentageBasedSampler,預設的采樣比例為: 0.1,即 10%。我們可以通過 spring.sleuth.sampler.probability 來設定,所設定的值介于 0 到 1 之間,1 則表示全部采集
3.4 啟動類
@SpringBootApplication
@RestController
public class ServiceHelloApplication {
public static void main(String[] args) {
SpringApplication.run(ServiceHelloApplication.class, args);
}
private static final Logger LOG = Logger.getLogger(ServiceHelloApplication.class.getName());
@RequestMapping(value = "/hello", method = RequestMethod.GET)
public String home(){
LOG.log(Level.INFO, "hello is being called");
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
}
throw new RuntimeException("測試異常");
// return "hi i'm service-hello!";
}
@RequestMapping(value = "/hello/hi", method = RequestMethod.GET)
public String info(){
LOG.log(Level.INFO, "hello/hi is being called");
return restTemplate.getForObject("http://localhost:8483/hi",String.class);
}
@Autowired
private RestTemplate restTemplate;
@Bean
public RestTemplate getRestTemplate(){
return new RestTemplate();
}
}
4. 建立 service-hi
4.1 service-hi 的 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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.example</groupId>
<artifactId>spring-cloud-sleuth-zipkin-demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<artifactId>service-hi</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>service-hi</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<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>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
<!--引入的zipkin依賴-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-zipkin</artifactId>
<version>2.1.1.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
4.2 配置檔案如下:
server:
port: 8482
spring:
application:
name: service-hi
rabbitmq:
host: 192.168.41.16
port: 5672
username: guest
password: guest
zipkin:
rabbitmq:
queue: zipkin
sleuth:
sampler:
#采樣率,推薦0.1,百分之百收集的話存儲可能扛不住
probability: 1.0 #Sleuth 預設采樣算法的實作是 Reservoir sampling,具體的實作類是 PercentageBasedSampler,預設的采樣比例為: 0.1,即 10%。我們可以通過 spring.sleuth.sampler.probability 來設定,所設定的值介于 0 到 1 之間,1 則表示全部采集
4.3 啟動類如下:
@SpringBootApplication
@RestController
public class ServiceHiApplication {
public static void main(String[] args) {
SpringApplication.run(ServiceHiApplication.class, args);
}
private static final Logger LOG = Logger.getLogger(ServiceHiApplication.class.getName());
@Autowired
private RestTemplate restTemplate;
@Bean
public RestTemplate getRestTemplate() {
return new RestTemplate();
}
@RequestMapping(value = "/hi", method = RequestMethod.GET)
public String info() {
LOG.log(Level.INFO, "calling trace service-hi ");
return "i'm service-hi";
}
@RequestMapping(value = "/hi/hello", method = RequestMethod.GET)
public String callHome() {
LOG.log(Level.INFO, "calling trace service-hello ");
return restTemplate.getForObject("http://localhost:8481/hello", String.class);
}
}
5. 驗證
依次啟動 zipkin-server、service-hello、service-hi,
通路 http://localhost:8481/hello/hi
通路 http://localhost:8482/hi/hello
6. 特性
接口通路已經成功,此時,我們檢視一下控制台的日志輸出:
從上面的控制台輸出内容中,我們可以看到多了一些如注意
[appname,traceId,spanId,exportable]
的日志資訊,而這些元素正是實作分布式服務跟蹤的重要組成部分,每個值的含義如下:
-
:service-hello,它記錄了應用的名稱appname
-
traceId
:3c15adfc71e4da46,是 Spring Cloud Sleuth 生成的一個 ID,稱為 Trace ID,它用來辨別一
請求鍊路。一條請求鍊路中包含一個 Trace ID,多個 Span ID。
-
:266c1eb7011e3baf,是 Spring Cloud Sleuth 生成的另外一個 ID,稱為 Span ID,它表示一個基本的工作單元,比如發送一個 HTTP 請求。spanId
-
:true,它表示是否要将該資訊輸出到 Zipkin Server 中來收集和展示。exportable
上面四個值中的 Trace ID 和 Span ID 是 Spring Cloud Sleuth 實作分布式服務跟蹤的核心。在一次請求中,會保持并傳遞同一個 Trace ID,進而将整個分布于不同微服務程序中的請求跟蹤資訊串聯起來。
下面我們通路 Zipkin Server 端,http://localhost:9411/
有些小夥伴第一次進來發現服務名下并沒有看到我們的應用,這是為什麼呢?
這是因為 Spring Cloud Sleuth 采用了抽樣收集的方式來為跟蹤資訊打上收集标記,也就是上面看到的第四個值。為什麼要使用抽樣收集呢?理論上應該是收集的跟蹤資訊越多越好,可以更好的反映出系統的實際運作情況,但是在高并發的分布式系統運作時,大量請求調用會産生海量的跟蹤日志資訊,如果過多的收集,會對系統性能造成一定的影響,是以 Spring Cloud Sleuth 采用了抽樣收集的方式。
既然如此,那麼我們就需要把上面第四個值改為 true,開發過程中,我們一般都是收集全部資訊。
Sleuth 預設采樣算法的實作是 Reservoir sampling,具體的實作類是 PercentageBasedSampler,預設的采樣比例為: 0.1,即 10%。我們可以通過 spring.sleuth.sampler.probability 來設定,所設定的值介于 0 到 1 之間,1 則表示全部采集
本文是直接設定
spring.sleuth.sampler.probability=1.0
,采樣率100%
鍊路追蹤詳情
7. demo源碼位址:
https://github.com/yanzhaoyao/spring-cloud-sleuth-zipkin-demo
具體的參考資料如下,推薦看spring cloud 官網的文檔,下面第一個
8. 參考資料:
https://cloud.spring.io/spring-cloud-sleuth/reference/html[推薦]
https://github.com/openzipkin/zipkin/tree/master/zipkin-server[推薦]
https://blog.csdn.net/hubo_88/article/details/80878632
https://blog.csdn.net/hubo_88/article/details/80889973
https://www.jianshu.com/p/4b9bf5a311fe
https://www.jianshu.com/p/4ea093c29c0e
https://www.cnblogs.com/lifeone/p/9040336.html