spring-cloud之服務跟蹤篇

why:
     1,微服務架構微服務增多,一個客戶端請求形成一個複雜的分佈式服務調用鏈路,如果任何一個服務延遲過高或錯誤,都會引起請求失敗。

how:
鏈接官網

引入了https://github.com/spring-cloud/spring-cloud-sleuth


1.分佈式服務先入爲主

 啓動相應example: eureka-server, trace-1,trace-2
2018-03-14 17:03:25.371  INFO [trace-1,f410ab57afd5c145,a9f2118fa2019684,false] 5224 --- [io-9101-exec-10] ication$$EnhancerBySpringCGLIB$$78d1577e : ===<call trace-1>===
2018-03-14 17:03:25.371  INFO [trace-2,f410ab57afd5c145,b789118fa2019777,false] 23112 --- [nio-9102-exec-1] ication$$EnhancerBySpringCGLIB$$e6cb4078 : ===<call trace-2>===

從上面的控制檯輸出內容中,我們可以看到形如[trace-1,f410ab57afd5c145,a9f2118fa2019684,false]的日誌信息,而這些元素正是實現分佈式服務跟蹤的重要組成部分,它們每個值的含義如下:
  • 第一個值:trace-1,它記錄了應用的名稱,也就是application.propertiesspring.application.name參數配置的屬性。
  • 第二個值:f410ab57afd5c145,Spring Cloud Sleuth生成的一個ID,稱爲Trace ID,它用來標識一條請求鏈路。一條請求鏈路中包含一個Trace ID,多個Span ID。
  • 第三個值:a9f2118fa2019684,Spring Cloud Sleuth生成的另外一個ID,稱爲Span ID,它表示一個基本的工作單元,比如:發送一個HTTP請求。
  • 第四個值:false,表示是否要將該信息輸出到Zipkin等服務中來收集和展示。
上面四個值中的Trace IDSpan ID是Spring Cloud Sleuth實現分佈式服務跟蹤的核心。在一次服務請求鏈路的調用過程中,會保持並傳遞同一個Trace ID,從而將整個分佈於不同微服務進程中的請求跟蹤信息串聯起來,以上面輸出內容爲例,trace-1trace-2同屬於一個前端服務請求來源,所以他們的Trace ID是相同的,處於同一條請求鏈路中。



2.分佈式跟蹤原理

分佈式系統中的服務跟蹤在理論上並不複雜,它主要包括下面兩個關鍵點:
  • 爲了實現請求跟蹤,當請求發送到分佈式系統的入口端點時,只需要服務跟蹤框架爲該請求創建一個唯一的跟蹤標識,同時在分佈式系統內部流轉的時候,框架始終保持傳遞該唯一標識,直到返回給請求方爲止,這個唯一標識就是前文中提到的Trace ID。通過Trace ID的記錄,我們就能將所有請求過程日誌關聯起來。
  • 爲了統計各處理單元的時間延遲,當請求達到各個服務組件時,或是處理邏輯到達某個狀態時,也通過一個唯一標識來標記它的開始、具體過程以及結束,該標識就是我們前文中提到的Span ID,對於每個Span來說,它必須有開始和結束兩個節點,通過記錄開始Span和結束Span的時間戳,就能統計出該Span的時間延遲,除了時間戳記錄之外,它還可以包含一些其他元數據,比如:事件名稱、請求信息等。
在Spring Boot應用中,通過在工程中引入spring-cloud-starter-sleuth依賴之後, 它會自動的爲當前應用構建起各通信通道的跟蹤機制,比如:
  • 通過諸如RabbitMQ、Kafka(或者其他任何Spring Cloud Stream綁定器實現的消息中間件)傳遞的請求
  • 通過Zuul代理傳遞的請求
  • 通過RestTemplate發起的請求
在快速入門示例中,由於trace-1trace-2發起的請求是通過RestTemplate實現的,所以spring-cloud-starter-sleuth組件會對該請求進行處理,在發送到trace-2之前sleuth會爲在該請求的Header中增加實現跟蹤需要的重要信息,主要有下面這幾個(更多關於頭信息的定義我們可以通過查看org.springframework.cloud.sleuth.Span的源碼獲取):
  • X-B3-TraceId:一條請求鏈路(Trace)的唯一標識,必須值
  • X-B3-SpanId:一個工作單元(Span)的唯一標識,必須值
  • X-B3-ParentSpanId::標識當前工作單元所屬的上一個工作單元,Root Span(請求鏈路的第一個工作單元)的該值爲空
  • X-B3-Sampled:是否被抽樣輸出的標誌,1表示需要被輸出,0表示不需要被輸出
  • X-Span-Name:工作單元的名稱

3.分佈式跟蹤(整合ElK,解決日誌分散在各個服務器上的問題

下面我們來詳細介紹一下在快速入門示例的基礎上,如何實現面向Logstash的日誌輸出配置:
  • pom.xml依賴中引入logstash-logback-encoder依賴
        
    <dependency>  
        <groupId>net.logstash.logback</groupId>
        <artifactId>logstash-logback-encoder</artifactId>
        <version>4.6</version>
    </dependency>
  • 在工程/resource目錄下創建bootstrap.properties配置文件,將spring.application.name=trace-1配置移動到該文件中去。由於logback-spring.xml的加載在application.properties之前,所以之前的配置logback-spring.xml無法獲取到spring.application.name屬性,因此這裏將該屬性移動到最先加載的bootstrap.properties配置文件中。
  • 在工程/resource目錄下創建logback配置文件logback-spring.xml
  • 對logstash支持主要通過名爲logstash的appender實現,內容並不複雜,主要是對日誌信息的格式化處理,上面爲了方便調試查看我們先將json日誌輸出到文件中。
  • 完成上面的改造之後,我們再將快速入門的示例運行起來,併發起對trace-1的接口訪問。此時我們可以在trace-1trace-2的工程目錄下發現有一個build目錄,下面分別創建了以各自應用名稱命名的json文件,該文件就是在logback-spring.xml中配置的名爲logstash的appender輸出的日誌文件,其中記錄了類似下面格式的json日誌
    {"@timestamp":"2018-03-14T09:03:09.434+00:00","severity":"DEBUG","service":"trace-2","trace":"28b1c8587cbc1ede","span":"8c09a75c740dafa5","exportable":"false","pid":"8460","thread":"http-nio-8080-exec-6","class":"o.s.web.servlet.DispatcherServlet","rest":"DispatcherServlet with name 'dispatcherServlet' processing GET request for [/trace-2]"}{"@timestamp":"2018-03-14T09:03:09.445+00:00","severity":"DEBUG","service":"trace-2","trace":"28b1c8587cbc1ede","span":"8c09a75c740dafa5","exportable":"false","pid":"8460","thread":"http-nio-8080-exec-6","class":"o.s.web.servlet.DispatcherServlet","rest":"Last-Modified value for [/trace-2] is: -1"}
    {"@timestamp":"2018-03-14T09:03:09.446+00:00","severity":"INFO","service":"trace-2","trace":"28b1c8587cbc1ede","span":"7f49a07b2c8ad929","exportable":"false","pid":"8460","thread":"http-nio-8080-exec-6","class":"c.d.TraceApplication$$EnhancerBySpringCGLIB$$e836b603","rest":"===<call trace-2, TraceId=28b1c8587cbc1ede, SpanId=8c09a75c740dafa5>==="}
    {"@timestamp":"2018-03-14T09:03:09.452+00:00","severity":"DEBUG","service":"trace-2","trace":"28b1c8587cbc1ede","span":"7f49a07b2c8ad929","exportable":"false","pid":"8460","thread":"http-nio-8080-exec-6","class":"o.s.web.servlet.DispatcherServlet","rest":"Null ModelAndView returned to DispatcherServlet with name 'dispatcherServlet': assuming HandlerAdapter completed request handling"}
    {"@timestamp":"2018-03-14T09:03:09.454+00:00","severity":"DEBUG","service":"trace-2","trace":"28b1c8587cbc1ede","span":"8c09a75c740dafa5","exportable":"false","pid":"8460","thread":"http-nio-8080-exec-6","class":"o.s.web.servlet.DispatcherServlet","rest":"Successfully completed request"}


  • 我們除了可以通過上面的方式生成json文件之外,也可以使用LogstashTcpSocketAppender將日誌內容直接通過Tcp Socket輸出到logstash服務端
    <appender name="logstash" class="net.logstash.logback.appender.LogstashTcpSocketAppender">
    <destination>127.0.0.1:9250</destination>
    ...
    </appender>


4.分佈式服務跟蹤(整合zipkin,解決對ELK缺少對請求鏈微服務延遲性的關注)

Zipkin是Twitter的一個開源項目,它基於Google Dapper實現。我們可以使用它來收集各個服務器上請求鏈路的跟蹤數據,並通過它提供的REST API接口來輔助我們查詢跟蹤數據以實現對分佈式系統的監控程序,從而及時地發現系統中出現的延遲升高問題並找出系統性能瓶頸的根源。除了面向開發的API接口之外,它也提供了方便的UI組件來幫助我們直觀的搜索跟蹤信息和分析請求鏈路明細,比如:可以查詢某段時間內各用戶請求的處理時間等。

上圖展示了Zipkin的基礎架構,它主要有4個核心組件構成:
  • Collector:收集器組件,它主要用於處理從外部系統發送過來的跟蹤信息,將這些信息轉換爲Zipkin內部處理的Span格式,以支持後續的存儲、分析、展示等功能。
  • Storage:存儲組件,它主要對處理收集器接收到的跟蹤信息,默認會將這些信息存儲在內存中,我們也可以修改此存儲策略,通過使用其他存儲組件將跟蹤信息存儲到數據庫中。
  • RESTful API:API組件,它主要用來提供外部訪問接口。比如給客戶端展示跟蹤信息,或是外接系統訪問以實現監控等。
  • Web UI:UI組件,基於API組件實現的上層應用。通過UI組件用戶可以方便而有直觀地查詢和分析跟蹤信息。

5.分佈式服務收集原理

數據模型+適當算法    

前面已經對如何引入Sleuth跟蹤信息和搭建Zipkin服務端分析跟蹤延遲的過程做了簡單的介紹,相信大家對於Sleuth和Zipkin已經有了一定的感性認識。接下來,我們介紹一下關於Zipkin收集跟蹤信息的過程細節,以幫助我們更好地理解Sleuth生產跟蹤信息以及輸出跟蹤信息的整體過程和工作原理


數據模型

我們先來看看Zipkin中關於跟蹤信息的一些基礎概念。由於Zipkin的實現借鑑了Google的Dapper,所以它們有着類似的核心術語,主要有下面幾個內容:
  • Span:它代表了一個基礎的工作單元。我們以HTTP請求爲例,一次完整的請求過程在客戶端和服務端都會產生多個不同的事件狀態(比如下面所說的四個核心Annotation所標識的不同階段),對於同一個請求來說,它們屬於一個工作單元,所以同一HTTP請求過程中的四個Annotation同屬於一個Span。每一個不同的工作單元都通過一個64位的ID來唯一標識,稱爲Span ID。另外,在工作單元中還存儲了一個用來串聯其他工作單元的ID,它也通過一個64位的ID來唯一標識,稱爲Trace ID。在同一條請求鏈路中的不同工作單元都會有不同的Span ID,但是它們的Trace ID是相同的,所以通過Trace ID可以將一次請求中依賴的所有依賴請求串聯起來形成請求鏈路。除了這兩個核心的ID之外,Span中還存儲了一些其他信息,比如:描述信息、事件時間戳、Annotation的鍵值對屬性、上一級工作單元的Span ID等。
  • Trace:它是由一系列具有相同Trace ID的Span串聯形成的一個樹狀結構。在複雜的分佈式系統中,每一個外部請求通常都會產生一個複雜的樹狀結構的Trace。
  • Annotation:它用來及時地記錄一個事件的存在。我們可以把Annotation理解爲一個包含有時間戳的事件標籤,對於一個HTTP請求來說,在Sleuth中定義了下面四個核心Annotation來標識一個請求的開始和結束:
    • cs(Client Send):該Annotation用來記錄客戶端發起了一個請求,同時它也標識了這個HTTP請求的開始。
    • sr(Server Received):該Annotation用來記錄服務端接收到了請求,並準備開始處理它。通過計算srcs兩個Annotation的時間戳之差,我們可以得到當前HTTP請求的網絡延遲。
    • ss(Server Send):該Annotation用來記錄服務端處理完請求後準備發送請求響應信息。通過計算sssr兩個Annotation的時間戳之差,我們可以得到當前服務端處理請求的時間消耗。
    • cr(Client Received):該Annotation用來記錄客戶端接收到服務端的回覆,同時它也標識了這個HTTP請求的結束。通過計算crcs兩個Annotation的時間戳之差,我們可以得到該HTTP請求從客戶端發起開始到接收服務端響應的總時間消耗。
  • BinaryAnnotation:它用來對跟蹤信息添加一些額外的補充說明,一般以鍵值對方式出現。比如:在記錄HTTP請求接收後執行具體業務邏輯時,此時並沒有默認的Annotation來標識該事件狀態,但是有BinaryAnnotation信息對其進行補充。

收集機制

在理解了Zipkin的各個基本概念之後,下面我們結合前面章節中實現的例子來詳細介紹和理解Spring Cloud Sleuth是如何對請求調用鏈路完成跟蹤信息的生產、輸出和後續處理的。
首先,我們來看看Sleuth在請求調用時是怎麼樣來記錄和生成跟蹤信息的。下圖展示了我們在本章節中實現示例的運行全過程:客戶端發送了一個HTTP請求到trace-1trace-1依賴於trace-2的服務,所以trace-1再發送一個HTTP請求到trace-2,待trace-2返回響應之後,trace-1再組織響應結果返回給客戶端。
在上圖的請求過程中,我們爲整個調用過程標記了10個標籤,它們分別代表了該請求鏈路運行過程中記錄的幾個重要事件狀態,我們根據事件發生的時間順序我們爲這些標籤做了從小到大的編號,1代表請求的開始、10代表請求的結束。每個標籤中記錄了一些我們上面提到過的核心元素:Trace ID、Span ID以及Annotation。由於這些標籤都源自一個請求,所以他們的Trace ID相同,而標籤1和標籤10是起始和結束節點,它們的Trace ID與Span ID是相同的。
根據Span ID,我們可以發現在這些標籤中一共產生了4個不同ID的Span,這4個Span分別代表了這樣4個工作單元:
  • Span T:記錄了客戶端請求到達trace-1trace-1發送請求響應的兩個事件,它可以計算出了客戶端請求響應過程的總延遲時間。
  • Span A:記錄了trace-1應用在接收到客戶端請求之後調用處理方法的開始和結束兩個事件,它可以計算出trace-1應用用於處理客戶端請求時,內部邏輯花費的時間延遲。
  • Span B:記錄了trace-1應用發送請求給trace-2應用、trace-2應用接收請求,trace-2應用發送響應、trace-1應用接收響應四個事件,它可以計算出trace-1調用trace-2的總體依賴時間(cr - cs),也可以計算出trace-1trace-2的網絡延遲(sr - cs),也可以計算出trace-2應用用於處理客戶端請求的內部邏輯花費的時間延遲(ss - sr)。
  • Span C:記錄了trace-2應用在接收到trace-1的請求之後調用處理方法的開始和結束兩個事件,它可以計算出trace-2應用用於處理來自trace-1的請求時,內部邏輯花費的時間延遲。
在圖中展現的這個4個Span正好對應了Zipkin查看跟蹤詳細頁面中的顯示內容,它們的對應關係如下圖所示:
仔細的讀者可能還有這樣一個疑惑:我們在Zipkin服務端查詢跟蹤信息時(如下圖所示),在查詢結果頁面中顯示的spans是5,而點擊進入跟蹤明細頁面時,顯示的Total Spans又是4,爲什麼會出現span數量不一致的情況呢?

實際上這兩邊的span數量內容有不同的含義,在查詢結果頁面中的5 spans代表了總共接收的Span數量,而在詳細頁面中的Total Span則是對接收Span進行合併後的結果,也就是前文中我們介紹的4個不同ID的Span內容。下面我們來詳細研究一下Zipkin服務端收集客戶端跟蹤信息的過程,看看它到底收到了哪些具體的Span內容,從而來理解Zipkin中收集到的Span總數量。

爲了更直觀的觀察Zipkin服務端的收集過程,我們可以對之前實現的消息中間件方式收集跟蹤信息的程序進行調試。通過在Zipkin服務端的消息通道監聽程序中增加斷點,我們就能清楚的知道客戶端都發送了一些什麼信息到Zipkin的服務端。在spring-cloud-sleuth-zipkin-stream依賴包中的代碼並不多,我們很容易的就能找到定義消息通道監聽的實現類:org.springframework.cloud.sleuth.zipkin.stream.ZipkinMessageListener。它的具體實現如下所示,其中SleuthSink.INPUT定義了監聽的輸入通道,默認會使用名爲sleuth的主題,我們也可以通過Spring Cloud Stream的配置對其進行修改。
@MessageEndpoint
@Conditional(NotSleuthStreamClient.class)
public class ZipkinMessageListener {
final Collector collector;

@ServiceActivator(inputChannel = SleuthSink.INPUT)
public void sink(Spans input) {
List<zipkin.Span> converted = ConvertToZipkinSpanList.convert(input);
this.collector.accept(converted, Callback.NOOP);
}
...

}

從通道監聽方法的定義中我們可以看到Sleuth與Zipkin在整合的時候是有兩個不同的Span定義的,一個是消息通道的輸入對象org.springframework.cloud.sleuth.stream.Spans,它是sleuth中定義的用於消息通道傳輸的Span對象,每個消息中包含的Span信息定義在org.springframework.cloud.sleuth.Span對象中,但是真正在zipkin服務端使用的並非這個Span對象,而是zipkin自己的zipkin.Span對象。所以,在消息通道監聽處理方法中,對sleuth的Span做了處理,每次接收到sleuth的Span之後就將其轉換成Zipkin的Span。
下面我們可以嘗試在sink(Spans input)方法實現的第一行代碼中加入斷點,並向trace-1發送一個請求,觸發跟蹤信息發送到RabbitMQ上。此時我們通過DEBUG模式可以發現消息通道中都接收到了兩次輸入,一次來自trace-1,一次來自trace-2。下面兩張圖分別展示了來自trace-1trace-2輸出的跟蹤消息,其中trace-1的跟蹤消息包含了3條span信息,trace-2的跟蹤消息包含了2條span信息,所以在這個請求調用鏈上,一共發送了5個span信息,也就是我們在Zipkin搜索結果頁面中看到的spans數量信息。
點開一個具體的Span內容,我們可以看到如下所示的結構,它記錄了Sleuth中定義的Span詳細信息,包括該Span的開始時間、結束時間、Span的名稱、Trace ID、Span ID、Tags(對應Zipkin中的BinaryAnnotation)、Logs(對應Zipkin中的Annotation)等我們之前提到過的核心跟蹤信息。
介紹到這裏仔細的讀者可能會有一個這樣的疑惑,在明細信息中展示的Trace ID和Span ID的值爲什麼與列表展示的概要信息中的Trace ID和Span ID的值不一樣呢?實際上,Trace ID和Span ID都是使用long類型存儲的,在DEBUG模式下查看其明細時自然是long類型,也就是它的原始值,但是在查看Span對象的時候,我們看到的是通過toString()函數處理過的值,從sleuth的Span源碼中我們可以看到如下定義,在輸出Trace ID和Span ID時都調用了idToHex函數將long類型的值轉換成了16進制的字符串值,所以在DEBUG時我們會看到兩個不一樣的值。
public String toString() {
return "[Trace: " + idToHex(this.traceId) + ", Span: " + idToHex(this.spanId)
+ ", Parent: " + getParentIdIfPresent() + ", exportable:" + this.exportable + "]";
}

public static String idToHex(long id) {
return Long.toHexString(id);
}

在接收到Sleuth之後我們將程序繼續執行下去,可以看到經過轉換後的Zipkin的Span內容,它們保存在一個名爲converted的列表中,具體內容如下所示:

上圖展示了轉換後的Zipkin Span對象的詳細內容,我們可以看到很多熟悉的名稱,也就是之前我們介紹的關於zipkin中的各個基本概念,而這些基本概念的值我們也都可以在之前sleuth的原始span中找到,其中annotations和binaryAnnotations有一些特殊,在sleuth定義的span中沒有使用相同的名稱,而是使用了logs和tags來命名。從這裏的詳細信息中,我們可以直觀的看到annotations和binaryAnnotations的作用,其中annotations中存儲了當前Span包含的各種事件狀態以及對應事件狀態的時間戳,而binaryAnnotations則存儲了對事件的補充信息,比如上圖中存儲的就是該HTTP請求的細節描述信息,除此之外,它也可以存儲對調用函數的詳細描述(如下圖所示)。

下面我們再來詳細看看通過調試消息監聽程序接收到的這5個Span內容。首先,我們可以發現每個Span中都包含有3個ID信息,其中除了標識Span自身的ID以及用來標識整條鏈路的traceId之外,還有一個之前沒有提過的parentId,它是用來標識各Span父子關係的ID(它的值來自與上一步執行單元Span的ID) ,通過parentId的定義我們可以爲每個Span找到它的前置節點,從而定位每個Span在請求調用鏈中的確切位置。在每條調用鏈路中都有一個特殊的Span,它的parentId爲null,這類Span我們稱它爲Root Span,也就是這條請求調用鏈的根節點。爲了弄清楚這些Span之間的關係,我們可以從Root Span開始來整理出整條鏈路的Span內容。下表展示了我們從Root Span開始,根據各個Span的父子關係最後整理出的結果:
Host
Span ID
Parent Span ID
Annotation
Binary Annotation
trace-1
e9a933ec50d180d6
null
[sr, ss]
 
trace-1
c48122fa096bffe8
e9a933ec50d180d6
 
[trace-1,TraceApplication,trace]
trace-1
1ae2e9a317faa422
c48122fa096bffe8
[cs, cr]
 
trace-2
1ae2e9a317faa422
c48122fa096bffe8
[sr, ss]
 
trace-2
36194e4182985c4e
1ae2e9a317faa422
 
[trace-2,TraceApplication,trace]
上表中的Host代表了該Span是從哪個應用發送過來的;Span ID是當前Span的唯一標識;Parent Span ID代表了上一執行單元的Span ID;Annotation代表了該Span中記錄的事件(這裏主要用來記錄HTTP請求的四個階段,表中內容作了省略處理,只記錄了Annotation名稱(sr代表服務端接收請求,ss代表服務端發送請求,cs代表客戶端發送請求,cr代表客戶端接收請求),省略了一些其他細節信息,比如服務名、時間戳、IP地址、端口號等信息);Binary Annotation代表了事件的補充信息(Sleuth的原始Span記錄更爲詳細,Zipkin的Span處理後會去掉一些內容,對於有Annotation標識的信息,不再使用Binary Annotation補充,在上表中我們只記錄了服務名、類名、方法名,同樣省略了一些其他信息,比如:時間戳、IP地址、端口號等信息)。
通過收集到的Zipkin Span詳細信息,我們很容易的可以將它們與本節開始時介紹的一次調用鏈路中的10個標籤內容聯繫起來:
  • Span ID = T的標籤有2個,分別是序號1和10,它們分別表示這次請求的開始和結束。它們對應了上表中ID爲e9a933ec50d180d6的Span,該Span的內容在標籤10執行結束後,由trace-1將標籤1和10合併成一個Span發送給Zipkin Server。
  • Span ID = A的標籤有2個,分別是序號2和9,它們分別表示了trace-1請求接收後,具體處理方法調用的開始和結束。該Span的內容在標籤9執行結束後,由trace-1將標籤2和9合併成一個Span發送給Zipkin Server。
  • Span ID = B的標籤有4個,分別是序號3、4、7、8,該Span比較特殊,它的產生跨越了兩個實例,其中標籤3和8是由trace-1生成的,而標籤4和7則是由trace-2生成的,所以該標籤會拆分成兩個Span內容發送給Zipkin Server。trace-1會在標籤8結束的時候將標籤3和8合併成一個Span發送給Zipkin Server,而trace-2會在標籤7結束的時候將標籤4和7合併成一個Span發送給Zipkin Server。
  • Span ID = C的標籤有2個,分別是序號5和6,它們分別表示了trace-2請求接收後,具體處理方法調用的開始和結束。該Span的內容在標籤6執行結束後,由trace-2將標籤2和9合併成一個Span發送給Zipkin Server。
所以,根據上面的分析,Zipkin總共會收到5個Span:一個Span T,一個Span A,兩個Span B,一個Span C。結合之前請求鏈路的標籤圖和這裏的Span記錄,我們可以總結出如下圖所示的Span收集過程,讀者可以參照此圖來理解Span收集過程的處理邏輯以及各個Span之間的關係。
雖然,Zipkin服務端接收到了5個Span,但就如前文中分析的那樣,其中有兩個Span ID=B的標籤,由於它們來自於同一個HTTP請求(trace-1trace-2的服務調用),概念上它們屬於同一個工作單元,因此Zipkin服務端在前端展現分析詳情時會將這兩個Span合併了來顯示,而合併後的Span數量就是在請求鏈路詳情頁面中Total Spans的數量。
下圖是本章示例的一個請求鏈路詳情頁面,在頁面中顯示了各個Span的延遲統計,其中第三條Span信息就是trace-1trace-2的HTTP請求調用,通過點擊它可以查看該Span的詳細信息,點擊後會以模態框的方式彈出Span詳細信息(如圖下半部分),在彈出框中詳細展示了Span的Annotation和BinaryAnnotation信息,在Annotation區域我們可以看到它同時包含了trace-1trace-2發送的Span信息,而在BinaryAnnotation區域則展示了該HTTP請求的詳細信息。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章