第九篇 : 服務鏈路追蹤(Spring Cloud Sleuth)
一、簡介
Spring Cloud Sleuth 主要功能就是在分佈式系統中提供追蹤解決方案,並且兼容支持了 zipkin,你只需要在pom文件中引入相應的依賴即可。
二、服務追蹤分析
微服務架構上通過業務來劃分服務的,通過REST調用,對外暴露的一個接口,可能需要很多個服務協同才能完成這個接口功能,如果鏈路上任何一個服務出現問題或者網絡超時,都會形成導致接口調用失敗。隨着業務的不斷擴張,服務之間互相調用會越來越複雜。
隨着服務的越來越多,對調用鏈的分析會越來越複雜。它們之間的調用關係也許如下:
三、術語
Span:基本工作單元,例如,在一個新建的span中發送一個RPC等同於發送一個迴應請求給RPC,span通過一個64位ID唯一標識,trace以另一個64位ID表示,span還有其他數據信息,比如摘要、時間戳事件、關鍵值註釋(tags)、span的ID、以及進度ID(通常是IP地址) span在不斷的啓動和停止,同時記錄了時間信息,當你創建了一個span,你必須在未來的某個時刻停止它。
Trace:一系列spans組成的一個樹狀結構,例如,如果你正在跑一個分佈式大數據工程,你可能需要創建一個trace。
Annotation:用來及時記錄一個事件的存在,一些核心annotations用來定義一個請求的開始和結束
cs - Client Sent -客戶端發起一個請求,這個annotion描述了這個span的開始
sr - Server Received -服務端獲得請求並準備開始處理它,如果將其sr減去cs時間戳便可得到網絡延遲
ss - Server Sent -註解表明請求處理的完成(當請求返回客戶端),如果ss減去sr時間戳便可得到服務端需要的處理請求時間
cr - Client Received -表明span的結束,客戶端成功接收到服務端的回覆,如果cr減去cs時間戳便可得到客戶端從服務端獲取回覆的所有所需時間 將Span和Trace在一個系統中使用Zipkin註解的過程圖形化:
將Span和Trace在一個系統中使用Zipkin註解的過程圖形化:
四、構建工程
基本知識講解完畢,下面我們來實戰,本文的案例主要有四個工程組成:一個server-zipkin,它的主要作用使用ZipkinServer 的功能,收集調用數據,並展示;一個eureka-server作爲服務註冊中心;一個service-hi,對外暴露hi接口;一個service-miya,對外暴露miya接口;這兩個service可以相互調用;並且只有調用了,server-zipkin纔會收集數據的,這就是爲什麼叫服務追蹤了。
1. 構建server-zipkin
在spring Cloud爲F版本的時候,已經不需要自己構建Zipkin Server了,只需要下載jar即可,下載地址:
https://dl.bintray.com/openzipkin/maven/io/zipkin/java/zipkin-server/
下載完成jar 包之後,需要運行jar,如下:
java -jar zipkin-server-2.9.4-exec.jar
訪問瀏覽器localhost:9411,顯示如下界面:
2. 創建 eureka-server
1. 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.gf</groupId>
<artifactId>eureka-server</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>eureka-server</name>
<description>Demo project for Spring Boot</description>
<parent>
<groupId>com.gf</groupId>
<artifactId>chapter09</artifactId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<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-server</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>
2. application.yml
# eureka.client.registerWithEureka :表示是否將自己註冊到Eureka Server,默認爲true。由於當前這個應用就是Eureka Server,故而設爲false
# eureka.client.fetchRegistry :表示是否從Eureka Server獲取註冊信息,默認爲true。因爲這是一個單點的Eureka Server,不需要同步其他的Eureka Server節點的數據,故而設爲false。
# eureka.client.serviceUrl.defaultZone :設置與Eureka Server交互的地址,查詢服務和註冊服務都需要依賴這個地址。默認是http://localhost:8761/eureka ;多個地址可使用 , 分隔。
server:
port: 8761
eureka:
instance:
hostname: localhost
client:
register-with-eureka: false
fetch-registry: false
service-url:
defaultZone: http://${eureka.instance.hostname}:/${server.port}/eureka/
spring:
application:
name: eureka-server
3. 主啓動類 EurekaServerApplication
package com.gf;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;
@EnableEurekaServer
@SpringBootApplication
public class EurekaServerApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaServerApplication.class, args);
}
}
3. 創建 service-hi
1. pom.xml
在其pom引入起步依賴spring-cloud-starter-zipkin,代碼如下:
<?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.gf</groupId>
<artifactId>service-hi</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>service-hi</name>
<description>Demo project for Spring Boot</description>
<parent>
<groupId>com.gf</groupId>
<artifactId>chapter09</artifactId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<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-server</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-ribbon</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-zipkin</artifactId>
</dependency>
</dependencies>
</project>
2. application.yml
在其配置文件application.yml指定zipkin server的地址,通過配置“spring.zipkin.base-url”指定:
server:
port: 8988
spring:
application:
name: service-hi
zipkin:
base-url: http://localhost:9411
eureka:
client:
service-url:
defaultZone: http://localhost:8761/eureka/
3. 主啓動類 ServiceHiApplication
package com.gf;
import brave.sampler.Sampler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
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;
@EnableEurekaClient
@SpringBootApplication
@RestController
public class ServiceHiApplication {
private static final Logger logger = LoggerFactory.getLogger( ServiceHiApplication.class );
@Autowired
private RestTemplate restTemplate;
public static void main(String[] args) {
SpringApplication.run(ServiceHiApplication.class, args);
}
@GetMapping("/hi")
public String hi() {
logger.info( "calling trace service-hi" );
return restTemplate.getForObject( "http://SERVICE-MIYA/miya" , String.class );
}
@GetMapping("/info")
public String info() {
logger.info( "calling trace service-hi " );
return "i'm service-hi";
}
@LoadBalanced
@Bean
RestTemplate restTemplate(){
return new RestTemplate();
}
/**
* 採樣率:
在生成環境中,由於業務量比較大,所產生的跟蹤數據可能會非常大,如果全部採集一是對業務有一定影響,二是對存儲壓力也會比較大,所以採樣變的很重要。
一般來說,我們也不需要把每一個發生的動作都進行記錄。
Spring Cloud Sleuth有一個Sampler策略,可以通過這個實現類來控制採樣算法。
採樣器不會阻礙span相關id的產生,但是會對導出以及附加事件標籤的相關操作造成影響。
Sleuth默認採樣算法的實現是Reservoir sampling,具體的實現類是PercentageBasedSampler,默認的採樣比例爲: 0.1(即10%)。
不過我們可以通過spring.sleuth.sampler.percentage來設置,所設置的值介於0.0到1.0之間,1.0則表示全部採集。
也可以通過實現bean的方式來設置採樣爲全部採樣(AlwaysSampler)或者不採樣(NeverSampler):如
*/
@Bean
public Sampler defaultSampler() {
return Sampler.ALWAYS_SAMPLE;
}
}
4. 創建service-miya
1. 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.gf</groupId>
<artifactId>service-miya</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>service-miya</name>
<description>Demo project for Spring Boot</description>
<parent>
<groupId>com.gf</groupId>
<artifactId>chapter09</artifactId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<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-server</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-zipkin</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-ribbon</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>
2. application.yml
server:
port: 8989
spring:
application:
name: service-miya
zipkin:
base-url: http://localhost:9411
eureka:
client:
service-url:
defaultZone: http://localhost:8761/eureka/
3. 主啓動類 ServiceMiyaApplication
package com.gf;
import brave.sampler.Sampler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
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
@EnableEurekaClient
@RestController
public class ServiceMiyaApplication {
private static final Logger logger = LoggerFactory.getLogger( ServiceMiyaApplication.class );
@Autowired
private RestTemplate restTemplate;
public static void main(String[] args) {
SpringApplication.run(ServiceMiyaApplication.class, args);
}
@GetMapping("/hi")
public String hi() {
logger.info( "hi is being called" );
return "hi i'm miya";
}
@GetMapping("/miya")
public String info() {
logger.info( "info is being called" );
return restTemplate.getForObject( "http://SERVICE-HI/info" , String.class );
}
@LoadBalanced
@Bean
RestTemplate restTemplate(){
return new RestTemplate();
}
@Bean
public Sampler defaultSampler() {
return Sampler.ALWAYS_SAMPLE;
}
}
五、啓動工程,演示追蹤
啓動上面的工程,訪問:http://localhost:8989/miya,瀏覽器出現:
i'm service-hi
再打開http://localhost:9411/的界面,點擊Dependencies,可以發現服務的依賴關係:
點擊find traces,可以看到具體服務相互調用的數據:
可以看到,Zipkin已經獲取到幾次服務的調用跟蹤信息了。我們可以點擊其中的一個請求,可以看到如下界面:
在該界面中我們可以看到之前所講的各個時間跟蹤信息。
源碼下載:https://github.com/gf-huanchupk/SpringCloudLearning
本文分享自微信公衆號 - 程序員果果(huanchuguofupk_gz)。
如有侵權,請聯繫 [email protected] 刪除。
本文參與“OSC源創計劃”,歡迎正在閱讀的你也加入,一起分享。