分散式跟蹤允許您跟蹤分散式系統中的請求。本文通過了解如何使用 Spring Cloud Sleuth 和 Zipkin 來做到這一點。
對於一個做所有事情的大型應用程式(我們通常將其稱為單體應用程式),跟蹤應用程式內的傳入請求很容易。我們可以跟蹤日誌,然後弄清楚請求是如何處理的。除了應用程式日誌本身之外,我們無需檢視其他任何內容。
隨著時間的推移,單體應用程式變得難以擴充套件,難以處理大量請求以及隨著程式碼庫規模的不斷擴大向客戶提供新功能。這導致將單體架構分解為微服務,這有助於擴充套件單個元件並有助於更快地交付。
但並非所有閃耀的都是黃金,對吧?微服務也是如此。我們將整個單體系統拆分為微服務,由一組本地函式呼叫處理的每個請求現在都被呼叫一組分散式服務所取代。這樣一來,我們就失去了追蹤在單體應用中很容易完成的請求之類的事情。現在,要跟蹤每個請求,我們必須檢視每個服務的日誌,並且很難關聯。
因此,在分散式系統的情況下,分散式跟蹤的概念有助於跟蹤請求。
什麼是分散式跟蹤?
分散式跟蹤是一種機制,我們可以使用它跟蹤整個分散式系統中的特定請求。它允許我們跟蹤請求如何從一個系統進展到另一個系統,從而完成使用者的請求。
分散式跟蹤的關鍵概念
分散式跟蹤包含兩個主要概念:
- 跟蹤 ID
- 跨度編號
跟蹤 id 用於跟蹤傳入請求並在所有組合服務中跟蹤它以滿足請求。Span id 跨越服務呼叫以跟蹤接收到的每個請求和發出的響應。
讓我們看一下圖表。
傳入的請求沒有任何跟蹤 ID。攔截呼叫的第一個服務會生成跟蹤 ID“ID1”及其跨度 ID“A”。span id“B”涵蓋了從伺服器一的客戶端發出請求到伺服器二接收、處理併發出響應的時間。
帶有 Spring Cloud Sleuth 的 Spring Boot 示例
讓我們建立一個整合了 Spring Cloud Sleuth 的應用程式。首先,讓我們訪問https://start.spring.io/並使用依賴項“Spring Web”和“Spring Cloud Sleuth”建立一個應用程式。
現在讓我們建立一個帶有兩個請求對映的簡單控制器。
public class Controller {
private static final Logger logger = LoggerFactory.getLogger(Controller.class);
private RestTemplate restTemplate;
@Value("${spring.application.name}")
private String applicationName;
public Controller(RestTemplate restTemplate) {
this.restTemplate = restTemplate;
}
@GetMapping("/path1")
public ResponseEntity path1() {
logger.info("Request at {} for request /path1 ", applicationName);
String response = restTemplate.getForObject("http://localhost:8090/service/path2", String.class);
return ResponseEntity.ok("response from /path1 + "+ response);
}
@GetMapping("/path2")
public ResponseEntity path2(){
logger.info("Request at {} at /path2", applicationName);
return ResponseEntity.ok("response from /path2 ");
}
在這裡,我建立了兩條路徑,Path1呼叫Path2固定埠 8090。這裡的想法是執行同一應用程式的兩個單獨例項。
現在為了允許偵探將標頭注入到傳出請求中,我們需要將 RestTemplate 作為 bean 注入,而不是直接初始化它。這將允許偵探向 RestTemplate 新增一個攔截器,以將帶有跟蹤 id 和跨度 id 的標頭注入到傳出請求中。
@Bean
public RestTemplate restTemplate(RestTemplateBuilder builder) {
return builder.build();
}
現在,讓我們啟動兩個例項。為此,首先,構建應用程式,mvn clean verify然後執行以下命令來啟動“服務 1”。
java -jar \target/Distributed-Service-0.0.1-SNAPSHOT.jar \--spring.application.name=Service-1 \--server.port=8080
然後在不同的終端上執行“服務 2”,如下所示:
java -jar \target/Distributed-Service-0.0.1-SNAPSHOT.jar \--spring.application.name=Service-2 \--server.port=8090
應用程式啟動後,呼叫“Service 1”,/path1如下所示:
curl -i http://localhost:8080/service/path1
現在讓我們看看“服務1”的日誌。
INFO [Service-1,222f3b00a283c75c,222f3b00a283c75c] 41114 --- [nio-8080-exec-1] c.a.p.distributedservice.Controller : Incoming request at Service-1 for request /path1
日誌包含方括號,其中包含三個部分 [Service Name, Trace Id, Span Id]。對於第一個傳入的請求,由於沒有傳入的trace id,span id 與trace id 相同。
檢視“服務 2”的日誌,我們看到我們為此請求有一個新的 span id。
INFO [Service-2,222f3b00a283c75c,13194db963293a22] 41052 --- [nio-8090-exec-1] c.a.p.distributedservice.Controller : Incoming request at Service-2 at /path2
我截獲了從“服務 1”傳送到“服務 2”的請求,並發現傳出的請求中已經存在以下標頭。
x-b3-traceid:"222f3b00a283c75c",
x-b3-spanid:"13194db963293a22",
x-b3-parentspanid:"222f3b00a283c75c
在這裡,我們看到下一個操作(對“服務 2”的呼叫)的跨度已經注入到標頭中。這些是在客戶端發出請求時由“服務 1”注入的。這意味著下一次呼叫“服務 2”的跨度已經從“服務 1”的客戶端開始。在上面顯示的標題中,“服務 1”的 span id 現在是下一個 span 的父 span id。
為了讓事情更容易理解,我們可以使用名為Zipkin的攔截器工具直觀地檢視跟蹤。
使用 Zipkin 視覺化跟蹤
要將 Zipkin 與應用程式整合,我們需要嚮應用程式新增 Zipkin 客戶端依賴項。
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-sleuth-zipkin</artifactId>
</dependency>
新增此依賴項後,Zipkin 客戶端預設將跟蹤傳送到 Zipkin 伺服器的 9411 埠。讓我們使用其 docker 映像啟動 Zipkin 伺服器。我為此建立了一個簡單的 docker-compose 檔案。
version: "3.1"
services:
zipkin:
image: openzipkin/zipkin:2
ports:
- "9411:9411"
我們現在可以使用docker-compose up命令啟動伺服器。然後,您可以在以下位置訪問 UIhttp://localhost:9411/
由於我們使用的是預設埠,我們不需要指定任何屬性,但是如果您打算使用不同的埠,則需要新增以下屬性。
spring:
zipkin:
baseUrl: http://localhost:9411
完成後,讓我們使用上面相同的命令啟動兩個應用程式。在向路徑中的“服務 1”發出請求時,/path1我們會得到以下跟蹤。
這裡顯示了兩個服務的跨度。我們可以通過檢視跨度來更深入地挖掘。
“服務 1”的跨度是一個正常的跨度,涵蓋了它接收到返回響應的請求。有趣的是第二個跨度。
在此,跨度中有四個點。
- 第一點是指來自“服務1”的客戶端何時開始請求。
- 第二點是“服務 2”開始處理請求的時間。
- 第三點是“Server 1”上的客戶端完成接收響應的時間。
- 最後,“伺服器 2”完成的最後一點。
因此,我們瞭解瞭如何將分散式跟蹤與 Spring Cloud Sleuth 整合,並使用 Zipkin 視覺化跟蹤。