1. 程式人生 > >誰說Cat不能做鏈路跟蹤的,給我站出來

誰說Cat不能做鏈路跟蹤的,給我站出來

## 背景 鏈路跟蹤,我們有很多可選項。常見的有 zipkin,pinpoint,skywalking,jaeger 等。 基本上都是根據谷歌的《Dapper 大規模分散式系統的跟蹤系統》這篇論文發展出來的。 今天講下 Cat 裡的鏈路跟蹤要如何來實現,沒用過 Cat 的同學可以檢視我的這篇文章 [《熬夜之作:一文帶你瞭解 Cat 分散式監控》](https://mp.weixin.qq.com/s/3mqmySr2nv4Xpd6nZlfsVg)進行了解。 在 Cat 中可以很方便的看到每個請求的總耗時以及業務操作,資料庫操作的耗時情況。對於服務之間的呼叫也可以通過埋點的方式進行監控。 如下圖,可以看出請求內發起了一次 RPC 的呼叫,callRPC 開頭的那條記錄。耗時 11ms, 但是這個 RPC 服務內部耗時花在哪裡了,在這邊不能直接檢視,只能去另一個服務中檢視,不是很方便。 ![](https://img2020.cnblogs.com/blog/1618095/202008/1618095-20200803122443125-822811777.png) 詳細的我畫了一張圖說明下現在的問題: ![](https://img2020.cnblogs.com/blog/1618095/202008/1618095-20200803122451905-1790032910.png) 從上圖可以看出,一個請求經過了多個服務,每個服務中對遠端呼叫或者本地呼叫都有埋點,這樣就能監控到呼叫的異常和效能指標。 下面一部分是在 Cat 中我們去檢視這些指標的場景,Cat 中的資料展示是以專案維度來展示的,所以每個服務都有自己的監控資料。 如果我想要知道剛剛那次請求,在整個鏈路中哪裡最慢,耗時在哪裡,我得分別去 4 個服務下面才能看到這些資訊,不直觀。 ## 實現方式 如下圖所示: 從閘道器到服務,從服務到服務,都需要將 Trace 資訊進行傳遞才可以將整個鏈路串起來。只有串起來了才可以在 Cat 中檢視到整個鏈路的耗時資訊。 ![](https://img2020.cnblogs.com/blog/1618095/202008/1618095-20200803122501801-1048780369.png) 本文需要實現的效果就是可以在請求的入口處(閘道器),檢視到這個請求經過的所有服務,每個服務中的耗時情況。 要想將整個請求都串連起來,必須要有一個唯一的請求標識,一般我們稱之為 traceId。剩餘的工作就是將鏈路相關的資訊層層傳遞下去。 首先在每個服務的過濾器中進行請求頭資訊的接收,比如從閘道器到服務 A,那麼服務 A 需要接收這些資訊然後傳遞給下一個服務。 HTTP 請求的訊息樹構建: ```plain // 構建遠端訊息樹if(request.getHeader(CatConstantsExt.CAT_HTTP_HEADER_ROOT_MESSAGE_ID) != null){ CatContext catContext = new CatContext(); catContext.addProperty(Cat.Context.ROOT,request.getHeader(CatConstantsExt.CAT_HTTP_HEADER_ROOT_MESSAGE_ID)); catContext.addProperty(Cat.Context.PARENT,request.getHeader(CatConstantsExt.CAT_HTTP_HEADER_PARENT_MESSAGE_ID)); catContext.addProperty(Cat.Context.CHILD,request.getHeader(CatConstantsExt.CAT_HTTP_HEADER_CHILD_MESSAGE_ID)); Cat.logRemoteCallServer(catContext); } ``` 將訊息樹的資訊傳遞給下個服務的話就要看你用的呼叫方式是什麼,如果用 Feign 或者 RestTemplate 都可以利用攔截器來實現傳遞。 ```plain public class FeignRequestInterceptor implements RequestInterceptor { @Override public void apply(RequestTemplate template) { CatContext catContext = new CatContext(); Cat.logRemoteCallClient(catContext,Cat.getManager().getDomain()); template.header(CatConstantsExt.CAT_HTTP_HEADER_ROOT_MESSAGE_ID, catContext.getProperty(Cat.Context.ROOT)); template.header(CatConstantsExt.CAT_HTTP_HEADER_PARENT_MESSAGE_ID, catContext.getProperty(Cat.Context.PARENT)); template.header(CatConstantsExt.CAT_HTTP_HEADER_CHILD_MESSAGE_ID, catContext.getProperty(Cat.Context.CHILD)); } } ``` 如果用的是 Dubbo 的話可以用 Dubbo 的 Filter 來實現相同的效果。 最終的效果如下圖,呼叫了 articles/newest 介面,閘道器將請求轉發到 article-provider 服務,article-provider 中又呼叫了 user-provider 的 users/uid 介面獲取使用者資訊,最重要的是 user-provider 中有哪些操作的耗時在這裡也能直觀的看到,非常方便。 ![](https://img2020.cnblogs.com/blog/1618095/202008/1618095-20200803122514075-1531803007.png) 完整原始碼參考: [https://github.com/yinjihuan/kitty](https://github.com/yinjihuan