問題場景
筆者之前一直使用SpringCloud Alibaba + dubbo2 + nacos1.4進行開發(fā),但是目前naocs2、dubbo3也已經推出有一段時間并逐漸達到生產環(huán)境可用狀態(tài),所以筆者也希望用最新版本的nacos2及dubbo3嘗嘗鮮。但在搭建框架的過程中遇到了鏈路追蹤的問題,所以在這里詳細記錄一下。
SpringCloud Alibaba + dubbo2 + nacos1
筆者基于dubbo2和nacos1的框架的依賴版本如下(這里只展示鏈路追蹤相關依賴):
SpringCloud Alibaba 2.2.5.RELEASE
Springboot 2.3.7.RELEASE
Dubbo 2.7.8
Nacos 1.4.3
spring-cloud-starter-sleuth 3.0.0
brave-instrumentation-dubbo 5.13.7
基于dubbo2和nacos1,該依賴版本網上都有較為成熟的搭建方案。直接引入依賴,按照度娘上的配置一下yml文件,就能實現(xiàn)鏈路追蹤,配置過程不太困難。這里就不詳細介紹了。
SpringCloud Alibaba + dubbo3 + nacos2
在搭建基于dubbo3和nacos2的框架時也希望能實現(xiàn)鏈路追蹤。其中方案一和方案二是目前Dubbo3官方手冊列舉的實現(xiàn)方案。方案一:Skywalking
聽說是最簡單的,但因為要額外部署Skywalking,所以我暫未嘗試這種方案。方案二:OpenTelemetry或brave
我根據(jù)dubbo3的文檔進行依賴的引入和yml文件的配置,但是發(fā)現(xiàn)服務提供者日志正常寫入了traceId和spanId,但服務消費者無論traceId還是spanId都沒寫入,這個在dubbo的GitHub issue中也未有人提問,所以最終也沒找到解決方案。
方案三:sleuth+brave
我嘗試了沿用SpringCloud Alibaba + dubbo2 + nacos1框架時的sleuth+brave方案實現(xiàn)鏈路追蹤,但我引入sleuth+brave并根據(jù)SpringCloud Alibaba + dubbo2 + nacos1框架時的yml文件配置后,發(fā)現(xiàn)雖然服務消費者和服務提供者的日志都寫入了traceId和spanId但兩個服務的traceId不一致,變成各寫各的了,整個追蹤鏈條并未正確串聯(lián)起來。 最終通過查找GitHub上的issue終于找到一個brave的dubbo2擴展能兼容dubbo3的解決方案,在這里感謝@ShenFeng312這位大佬。
GitHub issue的comment鏈接如下:
https://github.com/apache/dubbo/issues/11650#issuecomment-1446313706
解決方案
筆者用的依賴版本如下:
SpringCloud Alibaba 2021.0.5.0
Springboot 2.7.8
Dubbo 3.2.4
Nacos 2.2.0
spring-cloud-starter-sleuth 3.1.9
brave-instrumentation-dubbo 5.16.0
解決方案非常簡單,只需修改一行源碼即可。
修改brave.dubbo.TracingFilter#invoke中的RpcContext.getContext().getAttachments()改為invocation.getAttachments()
修改前:
修改后:
PS: 因為是要修改依賴的源碼,所以各位讀者可以復制該類、修改這行、增加Dubbo的SPI來指向自定義的TracingFilter。又或者自行重新打包jar。這里就不詳細敘述了。
原因
根據(jù)GitHub上https://github.com/apache/dubbo/issues/11650#issuecomment-1446313706中@ShenFeng312大佬的分析,主要是因為dubbo2和dubbo3的RpcContext中invcation的attachment屬性的實現(xiàn)方式改變了導致的。從issue的回復中其實也看到@ShenFeng312大佬也向brave提交了merge request,讓brave-instrumentation-dubbo能兼容dubbo3,但是brave的開發(fā)人員一直未同意合并,這里就不展開說了,大家有興趣可以自行去GitHub上看看。
疑問
根據(jù)知其然,也要知其所以然的想法,我嘗試分析其中的原因,但也產生了一些疑問,以下疑問也會在后續(xù)的排查過程中逐一解答。
疑問一:dubbo2中為什么直接用RpcContext.getContext().getAttachments()就能傳遞traceId而不用invocation.getAttachments()呢?
疑問二:為什么dubbo3中brave.dubbo.TracingFilter繼續(xù)用RpcContext.getContext().getAttachments()是不行的呢?
疑問三:dubbo3中的RpcContext.getContext().getAttachments()和invocation.getAttachments()有什么區(qū)別?
排查過程
追蹤brave.dubbo.TracingFilter#invoke方法的try…catch…部分可以看到,最終傳遞到下一個服務的參數(shù)是通過invocation的,而traceId和spanId是保存在invocation的attachment這個map中的。所以我們接下來排查的目標都是檢查并驗證最終invoke時invocation的attachment中有沒有traceId和spanId為準。
排查疑問一:
根據(jù)上述思路及brave.dubbo.TracingFilter#invoke方法中的注釋得知,其實clientHandler.handleSendWithParent(clientRequest, invocationContext)一直都只是在操作RpcContext.getContext()的attachment并未操作invocation中的attachment
而invocation中的attachment其實是直到invoker.invoke(invocation)時才在org.apache.dubbo.rpc.protocol.AbstractInvoker#invoke方法把RpcContext.getContext()的attachment注入到invocation中的attachment中
所以通過上面的代碼跟蹤可以發(fā)現(xiàn),dubbo2中雖然一直都是在操作RpcContext.getContext()的attachment,但會在AbstractInvoker#invoke方法中invoke下一個服務的前一刻把RpcCotext.getContext()的attachment注入到invocation中的attachment中,最終還是通過invocation中的attachment傳遞traceId給下一個服務的。
綜上所述,在dubbo2中通過RpcContext.getContext().getAttachments()來操作RpcContext的attachment最終都會在AbstractInvoker#invoke方法里被注入到invocation中的attachment中,所以dubbo2中是可以通過RpcContext.getContext().getAttachments()來傳遞traceId的
排查疑問二、三:
追蹤dubbo3中的RpcContext.getContext().getAttachments():
比對dubbo2中的RpcContext.getContext().getAttachments():
通過比較dubbo3和dubbo2的RpcContext.getContext().getAttachments()的實現(xiàn),可以發(fā)現(xiàn),dubbo3實現(xiàn)方式完全不同了,這個改動在dubbo3的官方手冊中其實是有提及的
正是因為RpcContext.getContext().getAttachments()的實現(xiàn)改動,dubbo3中RpcContext.getContext().getAttachments()返回的不是RpcContext中的attachment也不是invocation中的attachment。而是把RpcContext中的SERVER_ATTACHMENT、CLIENT_ATTACHMENT復制一份并返回。后面再怎么put參數(shù)進RpcContext中的attachment都沒有用了,即使后面在AbstractInvoker#invoke方法中把RpcContext中的attachment注入進invocation的attachment也沒用了,因為put參數(shù)的時候是put進RpcContext的attachment的副本,而不是RpcContext中的attachment本身
而且! 本身dubbo3中AbstractInvoker#invoke方法中把RpcContext中的attachment注入invocation的attachment的實現(xiàn)也變了
不過這里已經不重要了,因為put參數(shù)的時候是put進RpcContext中的attachment的副本,并未put進RpcContext的任何屬性中。
綜上所述,通過上述對源碼的分析也解釋了疑問二和疑問三,解釋了dubbo3中的RpcContext.getContext().getAttachments()和invocation.getAttachments()有什么區(qū)別和為什么dubbo3中brave.dubbo.TracingFilter繼續(xù)用RpcContext.getContext().getAttachments()是不行的。文章來源:http://www.zghlxwxcb.cn/news/detail-684328.html
總結分析
總的來說,主要是dubbo3的RpcContext被拆分為四大模塊才導致brave的dubbo擴展不能直接用,但是其實改動起來還是比較簡單的。上述如果有說的不對的地方,還請各位大佬指正。下面筆者還畫了一張圖來幫助理解:
最后,版權歸作者所有,任何形式轉載請聯(lián)系作者,謝謝!文章來源地址http://www.zghlxwxcb.cn/news/detail-684328.html
到了這里,關于dubbo3+sleuth+brave實現(xiàn)鏈路追蹤及traceId未傳遞或不對應的原因分析的文章就介紹完了。如果您還想了解更多內容,請在右上角搜索TOY模板網以前的文章或繼續(xù)瀏覽下面的相關文章,希望大家以后多多支持TOY模板網!