都在還說(shuō)鏈路跟蹤,那么 go-zero 的鏈路跟蹤是咋樣的?
工作中,自然少不了開(kāi)發(fā)去排查問(wèn)題,那如果鏈路比較長(zhǎng),客戶(hù)端一個(gè)請(qǐng)求打進(jìn)來(lái),可能內(nèi)部微服務(wù)進(jìn)行了多個(gè)服務(wù)的交互,那么如果其中有一個(gè)環(huán)節(jié)出現(xiàn)了問(wèn)題,我們?nèi)绾味ㄎ皇悄囊粋€(gè)請(qǐng)求或者是說(shuō)是哪一條調(diào)用鏈呢?
可能開(kāi)發(fā)的時(shí)候或多或少會(huì)加入自己的一些表示,例如請(qǐng)求里面會(huì)加入 requuid,鏈路中涉及的服務(wù)都會(huì)將這個(gè) requuid 傳下去,直到整個(gè)調(diào)用鏈結(jié)束
當(dāng)然比較 low 的一種方式是,在微服務(wù)交互的請(qǐng)求和相應(yīng)中加上 requuid 字段,這個(gè)應(yīng)該也是從單體服務(wù)過(guò)度到微服務(wù)開(kāi)發(fā)時(shí)特別容易出現(xiàn)的情況
實(shí)際上做鏈路跟蹤,簡(jiǎn)單的方式就是在各種請(qǐng)求的上下文加上 traceid 就可以了,今天我們來(lái)看看 go-zero 中的鏈路跟蹤是如何應(yīng)用的
分別從如下幾個(gè)部分來(lái)看看 go-zero 中應(yīng)用鏈路跟蹤
Http 服務(wù)端部分,客戶(hù)端部分
Rpc 服務(wù)端部分,客戶(hù)端部分
當(dāng)然關(guān)于日志組件,數(shù)據(jù)庫(kù)組件等相關(guān)組件,應(yīng)用到鏈路跟蹤的地方,做法大體一致,我們一起看看 http 和 rpc 服務(wù)中應(yīng)用的方式,其他的組件應(yīng)用咱們也就可以簡(jiǎn)單的將知識(shí)遷移過(guò)去即可上手
Http 服務(wù)端部分
對(duì)于 ge-zero http ,我們可以一起來(lái)看看 服務(wù)端部分是如何應(yīng)用的
簡(jiǎn)單來(lái)說(shuō),在我們啟動(dòng) http 服務(wù)的時(shí)候,就已經(jīng)將鏈路追蹤的功能給打開(kāi)了,go-zero 是通過(guò) http handle 的方式來(lái)處理的
當(dāng)然,同理,我們也可以自己在 go-zero 中加一個(gè) middleware 也是可以達(dá)到這樣的效果
go-zero 中實(shí)際上在我們服務(wù) start 的時(shí)候,內(nèi)部就開(kāi)始了各種初始化,大體流程是這樣的
(s *Server) Start() -> (ng *engine) start(router httpx.Router) error
調(diào)用內(nèi)部服務(wù)啟動(dòng),注冊(cè)路由
(ng *engine) bindRoutes(router httpx.Router) -> (ng *engine) bindFeaturedRoutes -> (ng *engine) bindRoute
進(jìn)行路由的綁定,并默認(rèn)添加中間件 middleware TracingHandler
我們可以看看關(guān)于 TracingHandler 的源碼實(shí)現(xiàn)
根據(jù)代碼,我們基本可以看出來(lái) TracingHandler 主要做了這幾件事情:
讀取 HeaderCarrier,獲取 header 中的上下文 ctx
如果傳入的 path 是空,則新建一個(gè) spanName
tracer.Start 開(kāi)始去處理 span,設(shè)置 span 的類(lèi)型,屬性
從
request
中產(chǎn)生新的ctx,并將相應(yīng)的信息封裝在 ctx 中,返回
Rpc 服務(wù)端部分,客戶(hù)端部分
那其實(shí) rpc 服務(wù)端和客戶(hù)端部分的實(shí)現(xiàn)和 http 的也是類(lèi)似的,http 使用的是中間件的方式來(lái)進(jìn)行處理,那么 rpc 這邊塊,其實(shí)可以通過(guò)攔截器的方式來(lái)進(jìn)行處理
服務(wù)端
使用 go-zero 項(xiàng)目的框架,我們?cè)趩?dòng) rpc 服務(wù)的時(shí)候,看看都做了些什么
服務(wù) start,go-zero 默認(rèn)就給我們的 rpc 服務(wù)添加了一些默認(rèn)的攔截器
例如 UnaryTracingInterceptor
UnaryCrashInterceptor 等等
其中 UnaryTracingInterceptor 不難看出就是用于做攔路追蹤的
而且我們可以看到 rpc 部分的攔截器分為流式的和非流式的,例如用于鏈路追蹤的還有 StreamTracingInterceptor
閱讀服務(wù)端 UnaryTracingInterceptor 攔截去源碼,我們可以知道實(shí)際上做法和 http server 部分的做法大體一致
通過(guò) rpc 的 ctx 處理 span
設(shè)置 span 的狀態(tài)和屬性等鍵值對(duì)
返回具體服務(wù)接口的數(shù)據(jù)
客戶(hù)端
那么客戶(hù)端有什么不一樣呢,其實(shí)也差不多
當(dāng)我們 api 層服務(wù)在初始化客戶(hù)端的時(shí)候就會(huì)使用 zrpc 包的 MustNewClient 方法
跟蹤代碼我們知道,最終客戶(hù)端的建立,會(huì)去和 rpc 服務(wù)端進(jìn)行建立連接,在建立連接的過(guò)程中會(huì)帶上一些參數(shù)
正式 buildDialOptions 在處理 options 的時(shí)候?qū)⒖蛻?hù)端的攔截器給默認(rèn)加上了 UnaryTracingInterceptor 和 StreamTracingInterceptor
那么 UnaryTracingInterceptor 的具體實(shí)現(xiàn)是這個(gè)樣的:
獲取上游帶下來(lái)的 span 上下文信息,startspan 中處理了關(guān)于 span 數(shù)據(jù)結(jié)構(gòu)中的各種鍵值對(duì)
從獲取的 span 中創(chuàng)建新的 ctx,span「繼承父span的traceId」
將生成 span 的data加入ctx,傳遞到下一個(gè)中間件,流至下游
關(guān)于 微服務(wù)框架 go-zero 的鏈路追蹤簡(jiǎn)單的流程咱們就先走到這里,有興趣的也可以把他用起來(lái)
【歡迎查看歷史文章】
微服務(wù)框架 go-zero logx 日志組件剖析
微服務(wù)框架 go-zero 快速實(shí)戰(zhàn)
感謝閱讀,歡迎交流,點(diǎn)個(gè)贊,關(guān)注一波 再走吧
歡迎點(diǎn)贊,關(guān)注,收藏
朋友們,你的支持和鼓勵(lì),是我堅(jiān)持分享,提高質(zhì)量的動(dòng)力
好了,本次就到這里
技術(shù)是開(kāi)放的,我們的心態(tài),更應(yīng)是開(kāi)放的。擁抱變化,向陽(yáng)而生,努力向前行。
我是阿兵云原生,歡迎點(diǎn)贊關(guān)注收藏,下次見(jiàn)~