我們?cè)谑褂梦⒎?wù)的時(shí)候,往往涉及到各個(gè)微服務(wù)之間的調(diào)用,肯定會(huì)存在深度的調(diào)用鏈路,如果出現(xiàn)BUG或者異常,就會(huì)讓問(wèn)題定位和處理效率非常低。有了Sleuth ,就可以幫助我們記錄、跟蹤應(yīng)用程序中的請(qǐng)求和操作。通常與 Zipkin 配合使用,從而提供更全面的可視化應(yīng)用程序跟蹤和分析功能。
就像ElasticSearch和Kibana一樣!
復(fù)雜的鏈路調(diào)用如下圖所示:
在繼續(xù)往下看的同時(shí),需要你具備Springboot整合Nacos構(gòu)建一個(gè)聚合項(xiàng)目的能力。
當(dāng)然如果不想自己來(lái),小編也給大家準(zhǔn)備好了。大家可以下載運(yùn)行一下,開(kāi)始下面的實(shí)戰(zhàn)!
防止Github訪(fǎng)問(wèn)不了,這里把代碼提交到了Gitee。
cloud-sleuth-zipkin-demo代碼下載地址:https://gitee.com/wang-zhenjun/cloud-sleuth-zipkin-demo
Spring Cloud Sleuth 是 Spring Cloud 生態(tài)系統(tǒng)的一部分,它是一個(gè)分布式追蹤解決方案,用于監(jiān)視微服務(wù)架構(gòu)中的請(qǐng)求流程,并幫助開(kāi)發(fā)者跟蹤請(qǐng)求在不同微服務(wù)之間的傳播路徑。
Sleuth主要用于解決微服務(wù)架構(gòu)中的分布式系統(tǒng)跟蹤和調(diào)試問(wèn)題。
官網(wǎng)文檔:https://docs.spring.io/spring-cloud-sleuth/docs/2.2.8.RELEASE/reference/html/。
我們可以看一下官網(wǎng)的圖片:
簡(jiǎn)單名詞介紹:
?
cs:「客戶(hù)端發(fā)送」,客戶(hù)已提出請(qǐng)求。該注釋指示跨度的開(kāi)始。sr:「服務(wù)器已接收」,服務(wù)器端收到請(qǐng)求并開(kāi)始處理。從此時(shí)間戳中減去cs時(shí)間戳即可得出網(wǎng)絡(luò)延遲。ss:「服務(wù)器發(fā)送」,在請(qǐng)求處理完成時(shí)(當(dāng)響應(yīng)發(fā)送回客戶(hù)端時(shí))進(jìn)行注釋。從這個(gè)時(shí)間戳中減去sr時(shí)間戳就可以得出服務(wù)器端處理請(qǐng)求所需的時(shí)間。cr:「客戶(hù)端已收到」,表示跨度的結(jié)束。客戶(hù)端已成功收到服務(wù)器端的響應(yīng)。從此時(shí)間戳中減去cs時(shí)間戳即可得出客戶(hù)端從服務(wù)器接收響應(yīng)所需的整個(gè)時(shí)間。
?
詳細(xì)信息可以看官網(wǎng)介紹,總結(jié)一下:
名詞 | 翻譯 | 解釋 |
Trace | 追蹤 | Trace 是一個(gè)請(qǐng)求的整體追蹤。它代表了從請(qǐng)求的起始點(diǎn)到結(jié)束點(diǎn)的完整路徑,經(jīng)過(guò)多個(gè)微服務(wù)。每個(gè) Trace 都有一個(gè)唯一的 Trace ID。 |
Span | 跨度 | Span 是 Trace 中的一個(gè)小段,它代表了請(qǐng)求在某個(gè)特定微服務(wù)上的處理過(guò)程。Spans 之間有父子關(guān)系,它們可以形成一個(gè)層次結(jié)構(gòu),以表示請(qǐng)求的處理路徑。 |
Trace ID | 追蹤標(biāo)識(shí) | Trace ID 是唯一標(biāo)識(shí)一個(gè) Trace 的標(biāo)識(shí)符。它在整個(gè) Trace 中保持不變,用于將不同的 Span 關(guān)聯(lián)到同一個(gè) Trace 上。 |
Span ID | 跨度標(biāo)識(shí) | Span ID 是唯一標(biāo)識(shí)一個(gè) Span 的標(biāo)識(shí)符。它用于在不同 Span 之間建立父子關(guān)系。 |
Parent Span ID | 父 Span 標(biāo)識(shí) | 父 Span ID 是標(biāo)識(shí)一個(gè) Span 的父 Span 的標(biāo)識(shí)符。它用于建立 Span 之間的關(guān)系。 |
Annotations | 注解 | Annotations 是關(guān)于 Span 的額外信息,通常用于記錄 Span 的開(kāi)始和結(jié)束時(shí)間、操作名稱(chēng)、以及其他相關(guān)信息。Annotations 可以幫助你更好地理解請(qǐng)求的處理過(guò)程。 |
Binary Annotations | 二進(jìn)制注解 | Binary Annotations 是鍵值對(duì)形式的信息,用于記錄與 Span 相關(guān)的自定義信息,例如請(qǐng)求的狀態(tài)、錯(cuò)誤信息等。 |
Collector | 收集器 | Collector 是用于收集追蹤信息的組件,它將追蹤數(shù)據(jù)發(fā)送到后端存儲(chǔ)或可視化工具(如Zipkin或Jaeger)。Collector 可以將 Span 數(shù)據(jù)持久化,以供分析和監(jiān)視使用。 |
Sampler | 采樣器 | Sampler 用于確定是否對(duì)一個(gè)請(qǐng)求進(jìn)行追蹤。它決定是否為請(qǐng)求創(chuàng)建一個(gè) Trace。Sampler 可以根據(jù)策略決定是否記錄某個(gè)請(qǐng)求的 Trace 數(shù)據(jù),以避免記錄過(guò)多的追蹤信息,從而降低性能開(kāi)銷(xiāo)。 |
「官網(wǎng)的圖,每一個(gè)代表一個(gè)組件,他們之間進(jìn)行調(diào)用,畫(huà)的少了Trace Id,加上就好了。」
每個(gè)組件都會(huì)生成一個(gè) Trace Id(全局唯一),還會(huì)有 Span Id、Parent Id 三部分組成。鏈路上的所有組件組成一個(gè)完整的 Trace。
「注意:」
「頭鏈路Parent Id = null,其余的都指向上一個(gè)組件的Span Id,從而形成鏈路。」
「一次鏈路調(diào)用所有的組件Trace Id都是一樣的。」
「這里說(shuō)的組件就是一個(gè)個(gè)的微服務(wù)!」
Zipkin 是一個(gè)分布式追蹤系統(tǒng)。它有助于收集解決服務(wù)架構(gòu)中的延遲問(wèn)題所需的計(jì)時(shí)數(shù)據(jù)。功能包括該數(shù)據(jù)的收集和查找。
Zipkin官網(wǎng)地址:https://zipkin.io/。
名詞 | 翻譯 | 解釋 |
Trace | 追蹤 | Trace 代表整個(gè)請(qǐng)求的追蹤路徑,跨越不同的服務(wù)。 |
Span | 跨度 | Span 是基本工作單位,代表了請(qǐng)求在單個(gè)服務(wù)中的處理過(guò)程。 |
Trace ID | 追蹤標(biāo)識(shí) | Trace ID 是唯一標(biāo)識(shí)一個(gè) Trace 的標(biāo)識(shí)符,用于將不同的 Span 關(guān)聯(lián)到同一個(gè) Trace 上。 |
Annotations | 注解 | Annotations 用于記錄 Span 的關(guān)鍵事件,通常包括開(kāi)始和結(jié)束時(shí)間、操作名稱(chēng)等。 |
Binary Annotations | 二進(jìn)制注解 | Binary Annotations 用于記錄額外的自定義信息,例如請(qǐng)求狀態(tài)、錯(cuò)誤信息等。 |
Collector | 收集器 | Collector 負(fù)責(zé)接收和存儲(chǔ)從不同服務(wù)發(fā)送的 Span 數(shù)據(jù),以便后續(xù)的檢查和分析。 |
Query and Visualization | 查詢(xún)和可視化 | 提供了查詢(xún)和可視化界面,允許用戶(hù)查看和分析跟蹤數(shù)據(jù),以幫助故障排查和性能優(yōu)化。 |
盡管Sleuth 和 Zipkin有些術(shù)語(yǔ)和概念中有相似之處,但它們是兩個(gè)不同的工具,各自有自己的實(shí)現(xiàn)和用途。
「Spring Cloud Sleuth 用于生成和傳播跟蹤信息,而 Zipkin 用于收集、存儲(chǔ)、查詢(xún)和可視化這些信息。它們可以協(xié)同工作,但也可以獨(dú)立使用。」
官方有三種方式搭建,推薦使用:「如果您熟悉 Docker,這是首選的啟動(dòng)方法。」
Docker Zipkin項(xiàng)目能夠構(gòu)建 docker 鏡像、提供腳本和docker-compose.yml 用于啟動(dòng)預(yù)構(gòu)建鏡像的腳本。
https://github.com/openzipkin/docker-zipkin/blob/master/docker-compose.yml。
最快的啟動(dòng)方式是直接運(yùn)行最新的鏡像:
docker run -d -p 9411:9411 openzipkin/zipkin
我們啟動(dòng)成功,在Windows下訪(fǎng)問(wèn)看是否成功!
http://192.168.239.130:9411/zipkin/
「Zipkin默認(rèn)將追蹤數(shù)據(jù)信息保存到內(nèi)存,重啟服務(wù)后追蹤數(shù)據(jù)丟失,Zipkin支持將追蹤數(shù)據(jù)持久化到MySQL或ES。」
可以直接使用docker-componse運(yùn)行:
docker-componse運(yùn)行腳本:https://github.com/openzipkin/zipkin/tree/master/docker/examples
可以自行試一下,這里就不帶大家演示了!
今天我們來(lái)進(jìn)行簡(jiǎn)單的鏈路模擬:
「service-order模塊調(diào)用service-stock模塊調(diào)用service-message模塊」
「通信我們使用openFeign來(lái)進(jìn)行調(diào)用,三個(gè)模塊統(tǒng)一使用nacos進(jìn)行注冊(cè)」
大家可以下載一下項(xiàng)目體驗(yàn)一下,可以自己搭建,就是一個(gè)聚合項(xiàng)目!
結(jié)構(gòu)如下:
這是父依賴(lài)。
<properties> <spring.boot.version>2.7.3</spring.boot.version> <spring.cloud.dependencies.version>2021.0.1</spring.cloud.dependencies.version> <spring.cloud.alibaba.version>2021.0.1.0</spring.cloud.alibaba.version> <java.version>1.8</java.version> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <org.projectlombok.lombok>1.18.26</org.projectlombok.lombok></properties><dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>${spring.cloud.dependencies.version}</version> <type>pom</type> <scope>import</scope> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-dependencies</artifactId> <version>${spring.boot.version}</version> <type>pom</type> <scope>import</scope> </dependency> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-alibaba-dependencies</artifactId> <version>${spring.cloud.alibaba.version}</version> <type>pom</type> <scope>import</scope> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> <version>${spring.boot.version}</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <version>${spring.boot.version}</version> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>${org.projectlombok.lombok}</version> </dependency> </dependencies></dependencyManagement>
spring-cloud-dependencies里包含了sleuth、zipkin的依賴(lài),父不需要再定義管理版本。
子依賴(lài)要比父依賴(lài)多了sleuth、zipkin兩個(gè),還有openFeign的包!
<!-- Sleuth依賴(lài)項(xiàng) --><dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-sleuth</artifactId></dependency><!--Zipkin 依賴(lài)--><dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-sleuth-zipkin</artifactId></dependency><dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId></dependency><dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-loadbalancer</artifactId></dependency>
三份自己改一下端口和應(yīng)用名稱(chēng):service-order、service-stock、service-message;端口分別為:9000、9001、9002
server: port: 9000spring: application: # 應(yīng)用名稱(chēng) name: service-order cloud: nacos: discovery: # 服務(wù)注冊(cè)地址 server-addr: localhost:8848 zipkin: base-url: http://192.168.239.130:9411 sender: type: web # 設(shè)置使用 http 的方式傳輸數(shù)據(jù)
記得在啟動(dòng)類(lèi)上添加注解:@EnableFeignClients,表示開(kāi)啟feign調(diào)用。
完整的結(jié)構(gòu)如下:
下面把具體代碼給大家:OrderController :
/** * @author wangzhenjun * @date 2023/10/31 14:25 */@Slf4j@RequiredArgsConstructor@RequestMapping("/order")@RestControllerpublic class OrderController { private final RemoteStockFeignService remoteStockFeignService; /** * 模擬下單流程 * @param userId * @param productId * @return */ @GetMapping("/createOrder") public String createOrder(@RequestParam("userId") Integer userId, @RequestParam("productId") Integer productId) { log.info("====>訂單模塊<========"); // 調(diào)用庫(kù)存服務(wù)進(jìn)行庫(kù)存扣減 String stockResult = remoteStockFeignService.subtractStock(userId,productId,1); log.info("扣減庫(kù)存結(jié)果:{}", stockResult); // 還有其他。。。。。 return "下單成功!"; }}
RemoteStockFeignService :
/** * @author wangzhenjun * @date 2023/10/31 14:29 */@FeignClient(value = "service-stock")public interface RemoteStockFeignService { @GetMapping(value = "/stock/subtractStock") String subtractStock(@RequestParam(value = "userId") Integer userId,@RequestParam(value = "productId") Integer productId,@RequestParam(value = "num") Integer num);}
StockController :
/** * @author wangzhenjun * @date 2023/10/31 14:40 */@Slf4j@RequiredArgsConstructor@RequestMapping("/stock")@RestControllerpublic class StockController { private final RemoteMessageFeignService remoteMessageFeignService; @GetMapping(value = "/subtractStock") public String subtractStock(@RequestParam(value = "userId") Integer userId,@RequestParam(value = "productId") Integer productId, @RequestParam(value = "num") Integer num) { log.info("====>庫(kù)存模塊<========"); if (productId < 1) { throw new RuntimeException("商品不存在,請(qǐng)重新請(qǐng)求!"); } // 調(diào)用短信模塊給用戶(hù)發(fā)下單成功短信 String messageResult = remoteMessageFeignService.sendMessage(userId); log.info("發(fā)送短信結(jié)果:{}", messageResult); return "扣減庫(kù)存成功!"; }}
RemoteMessageFeignService:
/** * @author wangzhenjun * @date 2023/10/31 14:29 */@FeignClient(value = "service-message")public interface RemoteMessageFeignService { @GetMapping(value = "/message/sendMessage/{userId}") String sendMessage(@PathVariable(value = "userId") Integer userId);}
MessageController :
/** * @author wangzhenjun * @date 2023/10/31 14:40 */@Slf4j@RequestMapping("/message")@RestControllerpublic class MessageController { @GetMapping(value = "/sendMessage/{userId}") public String sendMessage(@PathVariable(value = "userId") Integer userId) { log.info("====>短信模塊<========"); if (userId < 1 || userId > 999999) { throw new RuntimeException("用戶(hù)不存在,請(qǐng)重新請(qǐng)求!"); } return "發(fā)送短信成功!"; }}
Windows下啟動(dòng)nacos,找到nacos下的bin目錄執(zhí)行命令:
startup.cmd -m standalone
找到地址,訪(fǎng)問(wèn)。用戶(hù)名密碼都是nacos。可以在配置文件中修改!
我們把三個(gè)模塊進(jìn)行啟動(dòng)!nacos上已經(jīng)可以看到我們的服務(wù)注冊(cè)上了,可以通過(guò)服務(wù)名進(jìn)行調(diào)用了!
我們以訂單模塊為入口進(jìn)行鏈路調(diào)用:
http://localhost:9000/order/createOrder?userId=2&productId=89。
我們看一下訂單模塊的日志。
可以總結(jié)出Sleuth 日志格式:
[service-order,e36ebe859a7473e7,e36ebe859a7473e7]
[服務(wù)名稱(chēng),Trace ID,Span ID]
在最開(kāi)始的鏈路上,Trace ID 和 Span ID 的值通常是相同的,這是因?yàn)樗鼈兌即砹苏麄€(gè)請(qǐng)求的追蹤。
「一條鏈路使用一個(gè)相同的Trace ID。」
在日志沒(méi)有體現(xiàn)出Parent Span ID,不過(guò)不應(yīng)該,我們可以通過(guò)Zipkin來(lái)看鏈路!
前面我們已經(jīng)進(jìn)入了Zipkin頁(yè)面了,只需要刷新一下就可以看到每次鏈路的記錄了!
點(diǎn)擊SHOW按鈕,可以看到詳細(xì)鏈路信息:
耗時(shí),深度,Trance ID 還是挺好的。
我們來(lái)把商品修改一下:
http://localhost:9000/order/createOrder?userId=2&productId=-89。
此時(shí)是庫(kù)存服務(wù)出現(xiàn)的問(wèn)題,就不會(huì)展示下一個(gè)消息模塊,自然而然的找到了出現(xiàn)問(wèn)題的鏈路和根源!
在模擬一個(gè)三級(jí)錯(cuò)誤的,就會(huì)看到鏈路的最后一級(jí)!
下面還可以查詢(xún)依賴(lài)關(guān)系:
點(diǎn)擊節(jié)點(diǎn),可以查看匯總,調(diào)用次數(shù)和失敗次數(shù)的統(tǒng)計(jì)分析!
分布式鏈路追蹤已經(jīng)成為現(xiàn)代微服務(wù)架構(gòu)中不可或缺的工具之一。
通過(guò)它,我們可以清晰地跟蹤請(qǐng)求的調(diào)用路徑,了解系統(tǒng)的性能,診斷潛在問(wèn)題,并不斷優(yōu)化我們的應(yīng)用程序。
Spring Cloud Sleuth讓我們輕松生成和傳播跟蹤信息,使我們的微服務(wù)能夠協(xié)同工作,無(wú)縫地捕捉每個(gè)請(qǐng)求的處理路徑。
Zipkin作為一個(gè)流行的分布式追蹤系統(tǒng),為我們提供了可視化界面,使我們能夠以圖形化的方式查看和分析跟蹤數(shù)據(jù)。
「當(dāng)然簡(jiǎn)單系統(tǒng)上這個(gè)大材小用,但是我們可以在項(xiàng)目中試試,加了也不會(huì)影響程序的正常運(yùn)行,做一個(gè)簡(jiǎn)單的知識(shí)儲(chǔ)備!」
本文鏈接:http://www.tebozhan.com/showinfo-26-32008-0.html分布式進(jìn)階-鏈路追蹤SpringCloudSleuth、Zipkin【實(shí)戰(zhàn)篇】
聲明:本網(wǎng)頁(yè)內(nèi)容旨在傳播知識(shí),若有侵權(quán)等問(wèn)題請(qǐng)及時(shí)與本網(wǎng)聯(lián)系,我們將在第一時(shí)間刪除處理。郵件:2376512515@qq.com