書接上回:
在一個微服務架構中,系統的規模往往會比較大,各微服務之間的呼叫關係也錯綜複雜。通常一個有客戶端發起的請求在後端系統中會經過多個不同的微服務呼叫阿里協同產生最後的請求結果。在複雜的微服務架構中,幾乎每一個前端請求都會形成一條複雜的分散式的服務呼叫鏈路,在每條鏈路中任何一個依賴服務出現延遲過高或錯誤的時候都有可能引起請求最後的失敗。
這個時候,對於每個請求,全鏈路呼叫的跟蹤就邊得越來越重要,通過實現對請求呼叫的跟蹤可以幫助我們快速發現問題根源以及監控分析每條請求鏈路上的效能瓶頸等。而Spring Cloud Sleuth就是一個提供了一套完整的解決方案的元件。
在開始今天的這個例子之前,可以看一下我之前的幾篇部落格,特別是hystrix之前的部落格。本篇部落格就是在這基礎上所增加的新功能。在之前的實踐中,通過9004的customer-server專案呼叫9003的hello-server專案的介面。
準備工作
在之前的服務呼叫的方法上加上日誌操作。
customer-server的CustomerController類:
@RequestMapping("/sayHello1")
@ResponseBody
public String invokeSayHello1(String name){
logger.info("呼叫了customer-server的sayHello1方法,引數為:{}",name);
return serivce.invokeSayHello1(name);
}
hello-server的Hello1Controller類:
@RequestMapping("/sayHello1")
public String sayHello1(@RequestParam("name") String name){
logger.info("你好,服務名:{},埠為:{},接收到的引數為:{}",instanceName,host,name);
try {
int sleepTime = new Random().nextInt(3000);
logger.error("讓執行緒阻塞 {} 毫秒",sleepTime);
Thread.sleep(sleepTime);
} catch (InterruptedException e) {
e.printStackTrace();
}
return "你好,服務名:"+instanceName+",埠為:"+host+",接收到的引數為:"+name;
}
在頁面上訪問localhost:9004/sayHello1?name=charon
# customer-server中的列印日誌
2021-08-09 23:22:33.905 INFO 19776 --- [nio-9004-exec-8] c.c.e.controller.CustomerController : 呼叫了customer-server的sayHello1方法,引數為:charon
# hello-server中的列印日誌
2021-08-09 23:22:33.917 INFO 2884 --- [nio-9003-exec-9] c.c.e.controller.Hello1Controller : 你好,服務名:hello-server,埠為:9003,接收到的引數為:charon
實現跟蹤
在修改完上面的程式碼後,為customer-server專案和hello-server專案新增服務跟蹤的功能,引入依賴
<!--引入sleuth鏈路追蹤的jar包-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-sleuth</artifactId>
</dependency>
頁面呼叫檢視日誌:
# customer-server中的列印日誌
2021-08-09 23:30:44.782 INFO [customer-server,0e307552774ef605,0e307552774ef605,true] 14616 --- [nio-9004-exec-2] c.c.e.controller.CustomerController : 呼叫了customer-server的sayHello1方法,引數為:charon
# hello-server中的列印日誌
2021-08-09 23:30:44.807 INFO [hello-server,0e307552774ef605,4cf4d9dd57ca7478,true] 6660 --- [nio-9003-exec-2] c.c.e.controller.Hello1Controller : 你好,服務名:hello-server,埠為:9003,接收到的引數為:charon
從上面的控制檯的輸出內容可以看到形如[customer-server,0e307552774ef605,0e307552774ef605,true] 的日誌資訊,而浙西而元素正是實現分散式服務跟蹤的重要組成部分,每個值的含義如下:
- customer-server:應用的名稱,也就是application.properties中的soring。application.name的值
- 0e307552774ef605:Spring Cloud Sleuth生成的一個ID,成微Trace ID,它用來標識一條請求鏈路,一條請求鏈路中包含一個Trace ID,多個Span ID。
- 0e307552774ef605:Spring Cloud Sleuth生成的另一個ID,成為Span ID,它表識一個基本的工作單元,比如發慫一個HTTP請求
- true:表示是否要將改資訊輸出到Zipkin等服務中來收集和展示
在一個服務請求鏈路的呼叫過程中,會包吃並傳遞同一個Trace ID,從而將整個分佈於不容微服務程式中的請求跟蹤資訊串聯起來。以上面輸出內容為例,customer-server和hello-server同屬於一個前端服務請求來源,所以他們的Trace ID是相同的,處於同一個請求鏈路中。通過Trace ID,我們就能將所有請求過程的日誌關聯起來。
在Spring Boot應用中,通過引入spring-cloud-starter-sleuth依賴之後,他會自動為當前應用構建起通道跟蹤機制,比如:
- 通過RabbitMQ,Kafka等中介軟體傳遞的請求
- 通過Zuul代理傳遞的請求
- 通過RestTemplate發起的請求。
抽樣收集
通過TraceID和SpanID已經實現了對分散式系統中的請求跟蹤,而記錄的跟蹤資訊最終會被分析系統收集起來,並用來實現對分散式系統的監控和分析功能。
理論上講,收集的跟蹤資訊越多就可以越好的反應系統的真實執行情況,並給出更精準的預警和分析,但是在高併發的分散式系統執行時,大兩的請求呼叫會產生海量的跟蹤日誌資訊,如果收集過多對整個系統的效能也會造成一定的影響,同時儲存大兩的日誌資訊也需要很大的儲存開銷。所以在Sleuth中菜用了抽樣收集的方式來為跟蹤資訊打商收集標記。也就是我們之前在日誌資訊中看到的第4個布林型別的值,它代表了改資訊是否要改後續的跟蹤資訊收集器獲取或儲存。
預設情況下,Sleuth會使用 zipkin brave的ProbabilityBasedSampler的抽樣策略(現在已經不推薦使用),即以請求百分比的方式配置和收集跟蹤資訊,我們可以在配置檔案中配置引數對其百分比值進行設定(它的預設值為 0.1,代表收集 10% 的請求跟蹤資訊)。
spring.sleuth.sampler.probability=0.5
而如果在配置檔案中配置了 spring.sleuth.sampler.rate 的屬性值,那麼便會使用zipkin Brave自帶的RateLimitingSampler的抽樣策略。不同於ProbabilityBasedSampler菜用概況收集的策略,RateLimitingSampler是菜用的限速收集,也就是說它可以用來限制每秒跟蹤請求的最大數量。
- 如果同時設定了 spring.sleuth.sampler.rate 和 spring.sleuth.sampler.probability 屬性值,也仍然使用 RateLimitingSampler 抽樣策略(即 spring.sleuth.sampler.probability 屬性值無效)
- RateLimitingSampler 策略每秒間隔接受的 trace 量設定範圍:最小數字為 0,最大值為 2,147,483,647(最大 int)
整合Zipkin
Zipkin是twitter的一個開源專案,它基於Google Dapper實現,我們可以用它來實現收集各個伺服器上的請求鏈路的跟蹤。並通過它提供的REST API介面來輔助查詢跟蹤資料以實現對分散式系統的監控程式,從而及時發現系統中出現的延遲升高問題並找出系統效能瓶頸的根源。同時,Zipkin還提供了方便的UI元件來幫助我們直觀地所搜跟蹤資訊和分析請求地鏈路明細,比如可以查詢某段時間內各使用者請求地處理時間等。
Spring Boot 2.x 以後官網不推薦使用原始碼方式編譯,推薦使用官網編譯好的jar執行。所以我們不熟Zipkin也使用jar包的方式。
1.下載Zipkin
我這裡是到maven倉庫中下載的。
https://search.maven.org/remote_content?g=io.zipkin.java&a=zipkin-server&v=LATEST&c=exec
下載完成後,使用java -jar命令啟動zipkin。
2.引入依賴配置
為customer-server和hello-server的專案引入zipkin的包:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-sleuth-zipkin</artifactId>
</dependency>
配置檔案新增zipkin的地址:
spring.zipkin.base-url=http://localhost:9411
3.測試與分析
完成所有接入Zipkin的工作後,依次講服務起來,瀏覽器傳送請求做測試。
點選查詢按鈕,下方出現服務呼叫的資訊。注意,只有在sleuth的最後一個引數為true的時候,才會講改跟蹤資訊輸出給Zipkin Server。
單擊其中的某一個,還可以得到Sleuth跟蹤到的詳細資訊。其中就包括時間請求時間消耗等。
單擊導航欄中的依賴按鈕,還可以檢視到Zipkin根據跟蹤資訊分析生成的系統關係請求鏈路依賴關係圖。
持久化到mysql
在SpringBoot2.0之前的版本,Zipkin-Server端由我們自己建立專案來搭建。可以比較靈活的選擇資料持久化的配置,SpringBoot2.0之後的版本,Zipkin-Server端由官方提供,無需我們自己搭建,那麼如何選擇去配置將資料持久化到MySQL
呢?
1.建立zipkin資料庫
在下載好的zipkin-serve的jar包中,找到zipkin-server-shared.yml的檔案,
在裡面可以找到關於mysql的持久化配置,可以看到資料庫名稱預設為zipkin,
初始化mysql的指令碼:https://github.com/openzipkin/zipkin/blob/master/zipkin-storage/mysql-v1/src/main/resources/mysql.sql
建立的資料庫如下:
2.啟動zipkin
在啟動zipkin的時候,以命令列的方式啟動,輸入mysql的引數
java -jar zipkin-server-2.12.9-exec.jar --STORAGE_TYPE=mysql --MYSQL_HOST=127.0.0.1 --MYSQL_TCP_PORT=3306 --MYSQL_DB=zipkin --MYSQL_USER=root --MYSQL_PASS=root
3.測試與分析
瀏覽器訪問,因為我這次呼叫服務超時了,觸發了hystrix的斷路器功能,所以這次有8個span。
關閉zipkin-server,然後重啟,發現依然能夠查詢到上一次請求的服務鏈路跟蹤資料。檢視資料庫表,發現資料都儲存到表裡了。
參考文章:
翟永超老師的《Spring Cloud微服務實戰》