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

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

背景

鏈路跟蹤,我們有很多可選項。常見的有 zipkin,pinpoint,skywalking,jaeger 等。

基本上都是根據谷歌的《Dapper 大規模分散式系統的跟蹤系統》這篇論文發展出來的。

今天講下 Cat 裡的鏈路跟蹤要如何來實現,沒用過 Cat 的同學可以檢視我的這篇文章 《熬夜之作:一文帶你瞭解 Cat 分散式監控》進行了解。

在 Cat 中可以很方便的看到每個請求的總耗時以及業務操作,資料庫操作的耗時情況。對於服務之間的呼叫也可以通過埋點的方式進行監控。

如下圖,可以看出請求內發起了一次 RPC 的呼叫,callRPC 開頭的那條記錄。耗時 11ms, 但是這個 RPC 服務內部耗時花在哪裡了,在這邊不能直接檢視,只能去另一個服務中檢視,不是很方便。

詳細的我畫了一張圖說明下現在的問題:

從上圖可以看出,一個請求經過了多個服務,每個服務中對遠端呼叫或者本地呼叫都有埋點,這樣就能監控到呼叫的異常和效能指標。

下面一部分是在 Cat 中我們去檢視這些指標的場景,Cat 中的資料展示是以專案維度來展示的,所以每個服務都有自己的監控資料。

如果我想要知道剛剛那次請求,在整個鏈路中哪裡最慢,耗時在哪裡,我得分別去 4 個服務下面才能看到這些資訊,不直觀。

實現方式

如下圖所示:

從閘道器到服務,從服務到服務,都需要將 Trace 資訊進行傳遞才可以將整個鏈路串起來。只有串起來了才可以在 Cat 中檢視到整個鏈路的耗時資訊。

本文需要實現的效果就是可以在請求的入口處(閘道器),檢視到這個請求經過的所有服務,每個服務中的耗時情況。

要想將整個請求都串連起來,必須要有一個唯一的請求標識,一般我們稱之為 traceId。剩餘的工作就是將鏈路相關的資訊層層傳遞下去。

首先在每個服務的過濾器中進行請求頭資訊的接收,比如從閘道器到服務 A,那麼服務 A 需要接收這些資訊然後傳遞給下一個服務。

HTTP 請求的訊息樹構建:

// 構建遠端訊息樹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 都可以利用攔截器來實現傳遞。

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://github.com/yinjihuan/kitty