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