分散式鏈路追蹤之Spring Cloud Sleuth+Zipkin最全教程!

bucaichenmou發表於2021-11-18

大家好,我是不才陳某~

這是《Spring Cloud 進階》第九篇文章,往期文章如下:

今天這篇文章陳某介紹一下鏈路追蹤相關的知識,以Spring Cloud Sleuthzipkin這兩個元件為主,後續文章介紹另外一種。

文章的目錄如下:

為什麼需要鏈路追蹤?

大型分散式微服務系統中,一個系統被拆分成N多個模組,這些模組負責不同的功能,組合成一套系統,最終可以提供豐富的功能。在這種分散式架構中,一次請求往往需要涉及到多個服務,如下圖:

服務之間的呼叫錯綜複雜,對於維護的成本成倍增加,勢必存在以下幾個問題:

  • 服務之間的依賴與被依賴的關係如何能夠清晰的看到?
  • 出現異常時如何能夠快速定位到異常服務?
  • 出現效能瓶頸時如何能夠迅速定位哪個服務影響的?

為了能夠在分散式架構中快速定位問題,分散式鏈路追蹤應運而生。將一次分散式請求還原成呼叫鏈路,進行日誌記錄,效能監控並將一次分散式請求的呼叫情況集中展示。比如各個服務節點上的耗時、請求具體到達哪臺機器上、每個服務節點的請求狀態等等。

常見的鏈路追蹤技術有哪些?

市面上有很多鏈路追蹤的專案,其中也不乏一些優秀的,如下:

  • cat:由大眾點評開源,基於Java開發的實時應用監控平臺,包括實時應用監控,業務監控 。 整合方案是通過程式碼埋點的方式來實現監控,比如: 攔截器,過濾器等。 對程式碼的侵入性很大,整合成本較高,風險較大。

  • zipkin:由Twitter公司開源,開放原始碼分散式的跟蹤系統,用於收集服務的定時資料,以解決微服務架構中的延遲問題,包括:資料的收集、儲存、查詢和展現。該產品結合spring-cloud-sleuth使用較為簡單, 整合很方便, 但是功能較簡單。

  • pinpoint:韓國人開源的基於位元組碼注入的呼叫鏈分析,以及應用監控分析工具。特點是支援多種外掛,UI功能強大,接入端無程式碼侵入

  • skywalking:SkyWalking是本土開源的基於位元組碼注入的呼叫鏈分析,以及應用監控分析工具。特點是支援多種外掛,UI功能較強,接入端無程式碼侵入。目前已加入Apache孵化器。

  • Sleuth:SpringCloud 提供的分散式系統中鏈路追蹤解決方案。很可惜的是阿里系並沒有鏈路追蹤相關的開源專案,我們可以採用Spring Cloud Sleuth+Zipkin來做鏈路追蹤的解決方案。

Spring Cloud Sleuth是什麼?

Spring Cloud Sleuth實現了一種分散式的服務鏈路跟蹤解決方案,通過使用Sleuth可以讓我們快速定位某個服務的問題。簡單來說,Sleuth相當於呼叫鏈監控工具的客戶端,整合在各個微服務上,負責產生呼叫鏈監控資料。

Spring Cloud Sleuth只負責產生監控資料,通過日誌的方式展示出來,並沒有提供視覺化的UI介面。

學習Sleuth之前必須瞭解它的幾個概念:

  • Span:基本的工作單元,相當於連結串列中的一個節點,通過一個唯一ID標記它的開始、具體過程和結束。我們可以通過其中儲存的開始和結束的時間戳來統計服務呼叫的耗時。除此之外還可以獲取事件的名稱、請求資訊等。

  • Trace:一系列的Span串聯形成的一個樹狀結構,當請求到達系統的入口時就會建立一個唯一ID(traceId),唯一標識一條鏈路。這個traceId始終在服務之間傳遞,直到請求的返回,那麼就可以使用這個traceId將整個請求串聯起來,形成一條完整的鏈路。

  • Annotation:一些核心註解用來標註微服務呼叫之間的事件,重要的幾個註解如下:

    • cs(Client Send):客戶端發出請求,開始一個請求的生命週期
    • sr(Server Received):服務端接受請求並處理;sr-cs = 網路延遲 = 服務呼叫的時間
    • ss(Server Send):服務端處理完畢準備傳送到客戶端;ss - sr = 伺服器上的請求處理時間
    • cr(Client Reveived):客戶端接受到服務端的響應,請求結束; cr - sr = 請求的總時間

Spring Cloud 如何整合Sleuth?

整合Spring Cloud Sleuth其實沒什麼的難的,在這之前需要準備以下三個服務:

  • gateway-sleuth9031:作為閘道器服務
  • sleuth-product9032:商品微服務
  • sleuth-order9033:訂單微服務

三個服務的呼叫關係如下圖:

客戶端請求閘道器發起查詢訂單的請求,閘道器路由給訂單服務,訂單服務獲取訂單詳情並且呼叫商品服務獲取商品詳情。

新增依賴

在父模組中新增sleuth依賴,如下:

<dependency>
   <groupId>org.springframework.cloud</groupId>
   <artifactId>spring-cloud-starter-sleuth</artifactId>
</dependency>

以上只是Spring Cloud Sleuth的依賴,還有NacosopenFeign的依賴這裡就不再詳細說了,有不清楚的可以結合陳某前面幾篇文章和案例原始碼補漏一下。

調整日誌級別

由於sleuth並沒有UI介面,因此需要調整一下日誌級別才能在控制檯看到更加詳細的鏈路資訊。

在三個服務的配置檔案中新增以下配置:

## 設定openFeign和sleuth的日誌級別為debug,方便檢視日誌資訊
logging:
  level:
    org.springframework.cloud.openfeign: debug
    org.springframework.cloud.sleuth: debug

演示介面完善

以下介面只是為了演示造的資料,並沒有整合DB。

sleuth-order9033查詢訂單詳情的介面,如下圖:

sleuth-product9032的查詢商品詳情的介面,如下圖:

gateway-sleuth9031閘道器路由配置如下:

測試

啟動上述三個服務,瀏覽器直接訪問:http://localhost:9031/order/get/12

觀察控制檯日誌輸出,如下圖:

日誌格式中總共有四個引數,含義分別如下:

  • 第一個:服務名稱
  • 第二個:traceId,唯一標識一條鏈路
  • 第三個:spanId,鏈路中的基本工作單元id
  • 第四個:表示是否將資料輸出到其他服務,true則會把資訊輸出到其他視覺化的服務上觀察,這裡並未整合zipkin,所以是false

好了,至此整合完成了,不禁心裡倒吸一口涼氣,直接看日誌那不是眼睛要看瞎了..........

案例原始碼已經上傳,公號【碼猿技術專欄】回覆關鍵詞 9528獲取。

什麼是ZipKin?

Zipkin 是 Twitter 的一個開源專案,它基於Google Dapper實現,它致力於收集服務的定時資料,

以解決微服務架構中的延遲問題,包括資料的收集、儲存、查詢和展現

ZipKin的基礎架構如下圖:

Zipkin共分為4個核心的元件,如下:

  • Collector:收集器元件,它主要用於處理從外部系統傳送過來的跟蹤資訊,將這些資訊轉換為Zipkin內部處理的 Span 格式,以支援後續的儲存、分析、展示等功能。

  • Storage:儲存元件,它主要對處理收集器接收到的跟蹤資訊,預設會將這些資訊儲存在記憶體中,我們也可以修改此儲存策略,通過使用其他儲存元件將跟蹤資訊儲存到資料庫中

  • RESTful API:API 元件,它主要用來提供外部訪問介面。比如給客戶端展示跟蹤資訊,或是外接系統訪問以實現監控等。

  • UI:基於API元件實現的上層應用。通過UI元件使用者可以方便而有直觀地查詢和分析跟蹤資訊

zipkin分為服務端和客戶端,服務端主要用來收集跟蹤資料並且展示,客戶端主要功能是傳送給服務端,微服務的應用也就是客戶端,這樣一旦發生呼叫,就會觸發監聽器將sleuth日誌資料傳輸給服務端。

zipkin服務端如何搭建?

首先需要下載服務端的jar包,地址:https://search.maven.org/artifact/io.zipkin/zipkin-server/2.23.4/jar

下載完成將會得到一個jar包,如下圖:

直接啟動這個jar,命令如下:

java -jar zipkin-server-2.23.4-exec.jar

出現以下介面表示啟動完成:

此時可以訪問zipkin的UI介面,地址:http://localhost:9411,介面如下:

以上是通過下載jar的方式搭建服務端,當然也有其他方式安裝,比如docker,自己去嘗試一下吧,陳某就不再演示了。

zipKin客戶端如何搭建?

服務端只是跟蹤資料的收集和展示,客戶端才是生成和傳輸資料的一端,下面詳細介紹一下如何搭建一個客戶端。

還是上述例子的三個微服務,直接新增zipkin的依賴,如下:

<!--鏈路追蹤 zipkin依賴,其中包含Sleuth的依賴-->
<dependency>
      <groupId>org.springframework.cloud</groupId>
      <artifactId>spring-cloud-starter-zipkin</artifactId>
</dependency>

注意:由於spring-cloud-starter-zipkin中已經包含了Spring Cloud Sleuth依賴,因此只需要引入上述一個依賴即可。

配置檔案需要配置一下zipkin服務端的地址,配置如下:

spring:
  cloud:
  sleuth:
    sampler:
      # 日誌資料取樣百分比,預設0.1(10%),這裡為了測試設定成了100%,生產環境只需要0.1即可
      probability: 1.0
  zipkin:
      #zipkin server的請求地址
    base-url: http://127.0.0.1:9411
      #讓nacos把它當成一個URL,而不要當做服務名
    discovery-client-enabled: false

上述配置完成後啟動服務即可,此時訪問:http://localhost:9031/order/get/12

呼叫介面之後,再次訪問zipkin的UI介面,如下圖:

可以看到剛才呼叫的介面已經被監控到了,點選SHOW進入詳情檢視,如下圖:

可以看到左邊展示了一條完整的鏈路,包括服務名稱、耗時,右邊展示服務呼叫的相關資訊,包括開始、結束時間、請求url,請求方式.....

除了呼叫鏈路的相關資訊,還可以清楚看到每個服務的依賴如下圖,如下圖:

zipKin的資料傳輸方式如何切換?

zipkin預設的傳輸方式是HTTP,但是這裡存在一個問題,一旦傳輸過程中客戶端和服務端斷掉了,那麼這條跟蹤日誌資訊將會丟失。

當然zipkin還支援MQ方式的傳輸,支援訊息中介軟體有如下幾種:

  • ActiveMQ
  • RabbitMQ
  • Kafka

使用MQ方式傳輸不僅能夠保證訊息丟失的問題,還能提高傳輸效率,生產中推薦MQ傳輸方式

那麼問題來了,如何切換呢?

其實方式很簡單,下面陳某以RabbitMQ為例介紹一下。

1、服務端連線RabbitMQ

執行服務端並且連線RabbitMQ,命令如下:

java -jar zipkin-server-2.23.4-exec.jar --zipkin.collector.rabbitmq.addresses=localhost --zipkin.collector.rabbitmq.username=guest --zipkin.collector.rabbitmq.password=guest

命令分析如下:

  • zipkin.collector.rabbitmq.addresses:MQ地址
  • zipkin.collector.rabbitmq.username:使用者名稱
  • zipkin.collector.rabbitmq.password:密碼

2、客戶端新增RabbitMQ

既然使用MQ傳輸,肯定是要新增對應的依賴和配置了,新增RabbitMQ依賴如下:

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-amqp</artifactId>
</dependency>

配置MQ的地址、使用者名稱、密碼,配置如下:

spring:
  rabbitmq:
    addresses: 127.0.0.1
    username: guest
    password: guest

3、配置檔案中傳輸方式切換

spring.cloud.zipkin.sender.type這個配置就是用來切換傳輸方式的,取值為rabbit則表示使用rabbitMQ進行資料傳輸。

配置如下:

spring:
  cloud:
  zipkin:
    sender:
     ## 使用rabbitMQ進行資料傳輸
      type: rabbit

注意:使用MQ傳輸,則spring.cloud.zipkin.sender.base-url可以去掉。

完整的配置如下圖:

4、測試

既然使用MQ傳輸,那麼我們不啟動服務端也是能夠成功傳輸的,瀏覽器訪問:http://localhost:9031/order/get/12

此時發現服務並沒有報異常,在看RabbitMQ中已經有資料傳輸過來了,存在zipkin這個佇列中,如下圖:

可以看到有訊息未被消費,點進去可以看到訊息內容就是Trace、Span相關資訊。

好了,我們啟動服務端,命令如下:

java -jar zipkin-server-2.23.4-exec.jar --zipkin.collector.rabbitmq.addresses=localhost --zipkin.collector.rabbitmq.username=guest --zipkin.collector.rabbitmq.password=guest

服務端啟動後發現zipkin佇列中的訊息瞬間被消費了,檢視zipkin的UI介面發現已經生成了鏈路資訊,如下圖:

zipkin如何持久化?

zipkin的資訊預設是儲存在記憶體中,服務端一旦重啟資訊將會丟失,但是zipkin提供了可插拔式的儲存。

zipkin支援以下四種儲存方式:

  • 記憶體:服務重啟將會失效,不推薦
  • MySQL:資料量越大效能較低
  • Elasticsearch:主流的解決方案,推薦使用
  • Cassandra:技術太牛批,用的人少,自己選擇,不過官方推薦

今天陳某就以MySQL為例介紹一下zipkin如何持久化,Elasticsearch放在下一篇,篇幅有點長。

1、建立資料庫

zipkin服務端的MySQL建表SQL在原始碼中的zipkin-storage/mysql-v1/src/main/resources/mysql.sql中,這份SQL檔案我會放在案例原始碼中。

github地址:https://github.com/openzipkin/zipkin/blob/master/zipkin-storage/mysql-v1/src/main/resources/mysql.sql

建立的資料庫:zipkin(名稱任意),匯入建表SQL,新建的資料庫表如下圖:

2、服務端配置MySQL

服務端配置很簡單,執行如下命令:

java -jar zipkin-server-2.23.4-exec.jar --STORAGE_TYPE=mysql --MYSQL_HOST=127.0.0.1 --MYSQL_TCP_PORT=3306 --MYSQL_DB=zipkin --MYSQL_USER=root --MYSQL_PASS=Nov2014

上述命令引數分析如下:

  • STORAGE_TYPE:指定儲存的方式,預設記憶體形式
  • MYSQL_HOST:MySQL的ip地址,預設localhost
  • MYSQL_TCP_PORT:MySQL的埠號,預設埠3306
  • MYSQL_DB:MySQL中的資料庫名稱,預設是zipkin
  • MYSQL_USER:使用者名稱
  • MYSQL_PASS:密碼

陳某是如何記得這些引數的?廢話,肯定記不住,隨時檢視下原始碼不就得了,這些配置都在原始碼的/zipkin-server/src/main/resources/zipkin-server-shared.yml這個配置檔案中,比如上述MySQL的相關配置,如下圖:

zipkin服務端的所有配置項都在這裡,沒事去翻翻看。

github地址:https://github.com/openzipkin/zipkin/blob/master/zipkin-server/src/main/resources/zipkin-server-shared.yml

那麼採用rabbitMQ傳輸方式、MySQL持久化方式,完整的命令如下:

java -jar zipkin-server-2.23.4-exec.jar --STORAGE_TYPE=mysql --MYSQL_HOST=127.0.0.1 --MYSQL_TCP_PORT=3306 --MYSQL_DB=zipkin --MYSQL_USER=root --MYSQL_PASS=Nov2014 --zipkin.collector.rabbitmq.addresses=localhost --zipkin.collector.rabbitmq.username=guest --zipkin.collector.rabbitmq.password=guest

持久化是服務端做的事,和客戶端無關,因此到這就完事了,陳某就不再測試了,自己動手試試吧。

總結

前面介紹了這麼多,不知道大家有沒有仔細看,陳某總結一下吧:

  • Spring Cloud Sleuth 作為鏈路追蹤的一種元件,只提供了日誌採集,日誌列印的功能,並沒有視覺化的UI介面
  • zipkin提供了強大的日誌追蹤分析、視覺化、服務依賴分析等相關功能,結合Spring Cloud Sleuth作為一種主流的解決方案
  • zipkin生產環境建議切換的MQ傳輸模式,這樣做有兩個優點
    • 防止資料丟失
    • MQ非同步解耦,效能提升很大
  • zipkin預設是記憶體的形式儲存,MySQL雖然也是一種方式,但是隨著資料量越大,效能越差,因此生產環境建議採用Elasticsearch,下一篇文章介紹。

案例原始碼已經上傳,公號【碼猿技術專欄】回覆關鍵詞9528獲取。

最後說一句(求關注,別白嫖我)

陳某每一篇原創文章都是精心輸出,尤其是《Spring Cloud 進階》專欄的文章,知識點太多,要想講的細,必須要花很多時間準備,從知識點到原始碼demo。

如果這篇文章對你有所幫助,或者有所啟發的話,幫忙點贊在看轉發收藏,你的支援就是我堅持下去的最大動力!

相關文章