分布式鏈路追蹤(全鏈路追蹤)是分布式系統(tǒng)或者微服務(wù)架構(gòu)中服務(wù)監(jiān)控、性能優(yōu)化的有效手段。分布式鏈路追蹤有 Jaeger, Zipkin, SkyWalking 等方案喷面,我們?cè)敿?xì)講解它們的架構(gòu)原理仔沿;
- 分布式鏈路追蹤方案選型
- 分布式鏈路追蹤實(shí)踐
- 分布式鏈路追蹤 SDK
?向 DevOps 的診斷與分析系統(tǒng)
- Logging - 集中式?志系統(tǒng): ?于記錄離散的事件。例如逆巍,應(yīng)?程序的調(diào)試信息或錯(cuò)誤信息及塘。它是我們?cè)\斷問(wèn)題的依據(jù)。
- Tracing - 分布式追蹤系統(tǒng): ?于記錄請(qǐng)求范圍內(nèi)的信息锐极。例如笙僚,?次遠(yuǎn)程?法調(diào)?的執(zhí)?過(guò)程和耗時(shí)。它是我們排查系統(tǒng)性能問(wèn)題的利器灵再。
- Metrics - 集中式度量系統(tǒng): ?于記錄可聚合據(jù)肋层。的數(shù)例如,隊(duì)列的當(dāng)前深度可被定義為?個(gè)度量值翎迁,在元素?隊(duì)或出隊(duì)時(shí)被更新栋猖;HTTP 請(qǐng)求個(gè)數(shù)可被定義為?個(gè)計(jì)數(shù)器,新請(qǐng)求到來(lái)時(shí)進(jìn)?累加汪榔。
所以 Logging蒲拉,Metrics 和 Tracing 有各?專(zhuān)注的部分。
全鏈路 - 分布式追蹤
方案對(duì)比:
Drapper:google的Drapper--未開(kāi)源,最早的APM
CAT:?眾點(diǎn)評(píng)跨服務(wù)的跟蹤功能與點(diǎn)評(píng)內(nèi)部的RPC框架集成全陨,這部分未開(kāi)源且項(xiàng)?在2014.1已經(jīng)停?維護(hù)爆班。服務(wù)粒度的監(jiān)控,通過(guò)代碼埋點(diǎn)的?式來(lái)實(shí)現(xiàn)監(jiān)控辱姨,?如: 攔截器柿菩,注解,過(guò)濾器等雨涛,對(duì)代碼的侵?性較?枢舶,集成成本較?。
Hydra:京東Hydra與dubbo框架集成替久,對(duì)于服務(wù)級(jí)別的跟蹤統(tǒng)計(jì)凉泄,現(xiàn)有業(yè)務(wù)可以?縫接?。對(duì)于細(xì)粒度的興趣點(diǎn)蚯根,需要業(yè)務(wù)?員?動(dòng)添加.開(kāi)源項(xiàng)?已于2013年6?停?維護(hù)
PinPoint-naver:字節(jié)碼探針技術(shù)后众,代碼?侵?,體系完善不易修改颅拦,?持java,技術(shù)棧?持dubbo.其他語(yǔ)?社區(qū)?援中
Zipkin:java?便集成于springcloud蒂誉,社區(qū)?持的插件也包括dubbo,rabbit,mysql,httpclient等(https://github.com/openzipkin/brave/tree/master/instrumentation),同時(shí)?持php,go,js等語(yǔ)?客戶端距帅,界?功能較為簡(jiǎn)單右锨,本身?告警功能,可能需要?次開(kāi)發(fā)碌秸。代碼?侵度?绍移。
Jaeger:Uber-Jaeger?持java/c++/go/node/php,在界?上較為完善(對(duì)?zipkin)讥电,但是也?告警功能蹂窖。代碼?侵度?。dubbo?前?插件?持恩敌,可?次開(kāi)發(fā)瞬测。
Skywalking:華為開(kāi)源,類(lèi)似于PinPoint潮剪,?前還在apache孵化中,?上吞吐量對(duì)?中強(qiáng)于pinpoint (實(shí)際未驗(yàn)證), 本身?持dubbo
Jaeger
架構(gòu)設(shè)計(jì)
Jaeger是Uber開(kāi)發(fā)的?套分布式追蹤系統(tǒng)分唾,受啟發(fā)于 dapper 和OpenZipkin抗碰,兼容 OpenTracing 標(biāo)準(zhǔn),CNCF的開(kāi)源項(xiàng)?绽乔。
Jaeger 架構(gòu)設(shè)計(jì):
Jaeger Client - 為不同語(yǔ)?實(shí)現(xiàn)了符合 OpenTracing 標(biāo)準(zhǔn)的 SDK弧蝇。應(yīng)?程序通過(guò) API 寫(xiě)?數(shù)據(jù),client library 把 trace 信息按照應(yīng)?程序指定的采樣策略傳遞給 jaeger-agent。
Agent - 是?個(gè)監(jiān)聽(tīng)在 UDP 端?上接收 span 數(shù)據(jù)的?絡(luò)守護(hù)進(jìn)程看疗,它會(huì)將數(shù)據(jù)批量發(fā)送給 collector沙峻。它被設(shè)計(jì)成?個(gè)基礎(chǔ)組件,推薦部署到所有的宿主機(jī)上两芳。Agent 將 client library 和 collector 解耦摔寨,為 client library 屏蔽了路由和發(fā)現(xiàn) collector 的細(xì)節(jié)。
Collector - 接收 jaeger-agent 發(fā)送來(lái)的數(shù)據(jù)怖辆,然后將數(shù)據(jù)寫(xiě)?后端存儲(chǔ)是复。Collector 被設(shè)計(jì)成?狀態(tài)的組件,因此您可以同時(shí)運(yùn)?任意數(shù)量的 jaeger-collector竖螃。
Data Store - 后端存儲(chǔ)被設(shè)計(jì)成?個(gè)可插拔的組件淑廊,?持將數(shù)據(jù)寫(xiě)? cassandra、elastic search特咆。
Query - 接收查詢請(qǐng)求季惩,然后從后端存儲(chǔ)系統(tǒng)中檢索 trace 并通過(guò) UI 進(jìn)?展示。Query 是?狀態(tài)的腻格,您可以啟動(dòng)多個(gè)實(shí)例画拾,把它們部署在 nginx 這樣的負(fù)載均衡器后?。
Agent 初始化類(lèi)圖
TUDPTransport:基于Thrift框架的UDP傳輸類(lèi)荒叶。
TBufferedServer:基于TUDPTransport的UDP服務(wù)器類(lèi)碾阁。
Processor:消息處理類(lèi),提供Serve和Stop兩個(gè)接?些楣。
ThriftProcessor:消息傳遞類(lèi)脂凶,?于異步的將從UDPServer
這邊接收到的span消息,送?AgentProcessor處理愁茁。
AgentProcessor:根據(jù)協(xié)議區(qū)分jaeger和zipkin消息蚕钦,接收
并處理method為emitBatch的消息,發(fā)送?Reporter鹅很。
HTTPServerConfiguration:?于配置HTTPServer嘶居。
HTTPServer?于接收Collector的HTTP配置消息,配置采樣
率等信息
Agent初始化序列圖
CreateReporter:app/builder.go 中提供createMainReporter接?促煮,在接?中調(diào)?Builder.CreateReporter接?邮屁。CreateReporter接?在 app/report/tchannel/builder.go中實(shí)現(xiàn)。CreateReporter接?中創(chuàng)建了?個(gè)tchannel類(lèi)型的Reporter菠齿。
NewAgentProcessor:根據(jù)zipkin和jaeger兩種協(xié)議類(lèi)型佑吝,agent會(huì)創(chuàng)建各?的AgentProcessor。Jaeger類(lèi)型的AgentProcessor實(shí)現(xiàn)在 thrift-gen/agent/agent.go绳匀。得到傳回的對(duì)象后作為handler傳? ThriftProcessor芋忿。
getUDPServer:創(chuàng)建基于 thrift 的 UDP 服務(wù)器炸客,并作為?參傳? ThriftProcessor。
GetHTTPServer:創(chuàng)建基于HTTP的服務(wù)器戈钢,?于處理 Collector下發(fā)的配置痹仙,設(shè)置采樣率等信息。
Agent數(shù)據(jù)流序列圖
Serve:Agent為不同協(xié)議的ThriftProcessor創(chuàng)建多個(gè)協(xié)程殉了,并調(diào)?其Serve接?开仰。
processBuffer:在ThriftProcessor的Serve接?中根據(jù)配置創(chuàng)建多個(gè)協(xié)程?于并發(fā)處理span消息。
DataRecd:TBufferedServer在Buffer池的機(jī)制宣渗,避免了空間反復(fù)的 new 和 delete抖所。此處將?完的數(shù)據(jù)包返回TBufferedServer,以便下次接收數(shù)據(jù)時(shí)再次使?痕囱。
Process:ThriftProcessor將接收到的數(shù)據(jù)轉(zhuǎn)換成對(duì)應(yīng)的協(xié)議格式后田轧,傳遞到AgentProcessor中。
Process:AgentProcessor 解析消息 Method 頭鞍恢,如果為 EmitBatch 則調(diào)?對(duì)應(yīng)回調(diào)進(jìn)?處理傻粘。當(dāng)前只?持EmitBatch消息。
綜上所述帮掉,可以看出來(lái)Agent模塊主要通過(guò)tchannel接收本機(jī)的UDP消息(實(shí)為span消息)弦悉,再傳遞?thrift框架的Reporter,發(fā)送?Collector蟆炊。在Agent消息處理過(guò)程中稽莉,都為?進(jìn)制協(xié)議消息,?明?涩搓。Agent不對(duì)消息內(nèi)容做任何修改污秆。
SkyWalking
SkyWalking 架構(gòu)設(shè)計(jì)
SkyWalking 的核?是數(shù)據(jù)分析和度量結(jié)果的存儲(chǔ)平臺(tái),通過(guò) HTTP 或 gRPC ?式向SkyWalking Collecter 提交分析和度量數(shù)據(jù)昧甘,SkyWalking Collecter 對(duì)數(shù)據(jù)進(jìn)?分析和聚合良拼,存儲(chǔ)到 Elasticsearch、H2充边、MySQL庸推、TiDB 等其?即可,最后我們可以通過(guò) SkyWalking UI 的可視化界?對(duì)最終的結(jié)果進(jìn)?查看浇冰。SkyWalking ?持從多個(gè)來(lái)源和多種格式收集數(shù)據(jù):多種語(yǔ)?的 Skywalking Agent 贬媒、Zipkin v1/v2 、Istio 勘測(cè)肘习、Envoy 度量等數(shù)據(jù)格式际乘。
- SkyWalking 是針對(duì)分布式系統(tǒng)的 APM 系統(tǒng),也被稱(chēng)為分布式追蹤系統(tǒng)全?動(dòng)探針監(jiān)控井厌,不需要修改應(yīng)?程序代碼蚓庭。(查看?持的中間件和組件庫(kù)列表:https://github.com/apache/incubator/skywalking )
- ?持?動(dòng)探針監(jiān)控, 提供了?持 OpenTracing 標(biāo)準(zhǔn)的SDK。覆蓋范圍擴(kuò)?到 OpenTracing-Java ?持的組件仅仆。 (查看OpenTracing組件?持表:https://github.com/opentracing-contrib/meta )
- ?動(dòng)監(jiān)控和?動(dòng)監(jiān)控可以同時(shí)使?器赞,使??動(dòng)監(jiān)控彌補(bǔ)?動(dòng)監(jiān)控不?持的組件,甚?私有化組件墓拜。純 Java 后端分析程序港柜,提供 RESTful 服務(wù),可為其他語(yǔ)?探針提供分析能?咳榜。?性能純流式分析夏醉。
SkyWalking 邏輯上分為四部分: 探針, 平臺(tái)后端, 存儲(chǔ)和?戶界?。
探針:基于不同的來(lái)源可能是不?樣的, 但作?都是收集數(shù)據(jù), 將數(shù)據(jù)格式化為 SkyWalking 適?的格式
平臺(tái)后端:是?個(gè)?持集群模式運(yùn)?的后臺(tái), ?于數(shù)據(jù)聚合, 數(shù)據(jù)分析以及驅(qū)動(dòng)數(shù)據(jù)流從探針到?戶界?的流程. 平臺(tái)后端還提供了各種可插拔的能?, 如不同來(lái)源數(shù)據(jù)(如來(lái)? Zipkin)格式化, 不同存儲(chǔ)系統(tǒng)以及集群管理. 你甚?還可以使?觀測(cè)分析語(yǔ)?來(lái)進(jìn)??定義聚合分析
存儲(chǔ):是開(kāi)放式的. 你可以選擇?個(gè)既有的存儲(chǔ)系統(tǒng), 如 ElasticSearch, H2 或 MySQL 集群
(Sharding-Sphere 管理), 也可以選擇??實(shí)現(xiàn)?個(gè)存儲(chǔ)系統(tǒng). 當(dāng)然, 我們?常歡迎你貢獻(xiàn)新的存儲(chǔ)系統(tǒng)實(shí)現(xiàn)?戶界?:對(duì)于 SkyWalking 的最終?戶來(lái)說(shuō)?常炫酷且強(qiáng)?. 同樣它也是可定制以匹配你已存在的后端的
探針限制
進(jìn)程內(nèi)傳播在?多數(shù)情況下成為可能涌韩。許多?級(jí)編程語(yǔ)?(如 Java, .NET)都是?于構(gòu)建業(yè)務(wù)系統(tǒng). ?部分業(yè)務(wù)邏輯代碼對(duì)于每?個(gè)請(qǐng)求來(lái)說(shuō)都運(yùn)?在同?個(gè)線程內(nèi), 這使得傳播是基于線程 ID 的, 以確保上下?是安全的.僅僅對(duì)某些框架和庫(kù)奏效畔柔。因?yàn)槭谴韥?lái)在運(yùn)?時(shí)修改代碼的, 這也意味著代理插件開(kāi)發(fā)者事先就要知道 所要修改的代碼是怎么樣的. 因此, 在這種探針下通常會(huì)有?個(gè)已?持的列表清單.?持服務(wù)列表: https://github.com/apache/skywalking/blob/master/docs/en/setup/service-agent/java-agent/Supported-list.md
跨線程可能并?總是奏效 如上所述, 每個(gè)請(qǐng)求的代碼?都運(yùn)?在?個(gè)線程之內(nèi), 對(duì)于業(yè)務(wù)代碼來(lái)說(shuō)尤其如此. 但是在其他?些場(chǎng)景下, 它們也會(huì)在不同線程下?作, ?如指派任務(wù)到其他線程, 任務(wù)池, 以及批處理. 對(duì)于?些語(yǔ)?, 可能還提供了協(xié)程或類(lèi)似的概念如 Goroutine, 使得開(kāi)發(fā)者可以低開(kāi)銷(xiāo)地來(lái)執(zhí)?異步操作, 在這些場(chǎng)景下, ?動(dòng)打點(diǎn)可能會(huì)遇到?些問(wèn)題。
動(dòng)態(tài)探針技術(shù)
字節(jié)碼增加技術(shù)(有的叫動(dòng)態(tài)探針技術(shù))來(lái)實(shí)現(xiàn)?侵?式的調(diào)?鏈采集臣樱。其核?實(shí)現(xiàn)原來(lái)還是基于JVM的javaagent機(jī)制來(lái)實(shí)現(xiàn):
"-javaagent:$AGENT_PATH/-bootstrap-$VERSION.jar "
來(lái)指定skywalking agent加載路徑靶擦,在啟動(dòng)的時(shí)候agent將在加載應(yīng)?class?件之前做攔截并修改字節(jié)碼,在class?法調(diào)?的前后加上鏈路采集邏輯雇毫,從?實(shí)現(xiàn)鏈路采集功能玄捕。
javaAgent的底層機(jī)制主要依賴(lài)JVMTI ,JVMTI全稱(chēng)JVM Tool Interface棚放,是JVM暴露出來(lái)的?些供?戶擴(kuò)展的接?集合枚粘。JVMTI是基于事件驅(qū)動(dòng)的,JVM每執(zhí)?到?定的邏輯就會(huì)調(diào)??些事件的回調(diào)接?(如果有的話)飘蚯,這些接?可以供開(kāi)發(fā)者擴(kuò)展??的邏輯馍迄。但JVMTI都是?些接?合集,需要有接?的實(shí)現(xiàn)孝冒,這就?到了java的instrument柬姚,可以理解instrument是JVMTI的?種實(shí)現(xiàn),為JVM提供外掛?持庄涡。
javaagent具體實(shí)現(xiàn)
使?:java -javaagent:myagent.jar=mode=test Test
javaagent 的主要功能如下:
? 可以在加載 class ?件之前做攔截量承,對(duì)字節(jié)碼做修改
? 可以在運(yùn)?期對(duì)已加載類(lèi)的字節(jié)碼做變更,但是這種情況下會(huì)有很多的限制穴店,后?會(huì)詳細(xì)說(shuō)
? 還有其他?些?眾的功能
?獲取所有已經(jīng)加載過(guò)的類(lèi)
?獲取所有已經(jīng)初始化過(guò)的類(lèi)(執(zhí)?過(guò) clinit ?法撕捍,是上?的?個(gè)?集)
?獲取某個(gè)對(duì)象的??
?將某個(gè) jar 加?到 bootstrap classpath ?作為?優(yōu)先級(jí)被 bootstrapClassloader 加載
?將某個(gè) jar 加?到 classpath ?供 AppClassloard 去加載
?設(shè)置某些 native ?法的前綴,主要在查找 native ?法的時(shí)候做規(guī)則匹配
JVMTI 全稱(chēng) JVM Tool Interface泣洞,是 JVM 暴露出來(lái)的?些供?戶擴(kuò)展的接?集合忧风。JVMTI 是基于事件驅(qū)動(dòng)的,JVM 每執(zhí)?到?定的邏輯就會(huì)調(diào)
??些事件的回調(diào)接?(如果有的話)球凰,這些接?可以供開(kāi)發(fā)者擴(kuò)展??的邏輯狮腿。?如最常?的腿宰,我們想在某個(gè)類(lèi)的字節(jié)碼?件讀取之后、類(lèi)
定義之前修改相關(guān)的字節(jié)碼缘厢,從?使創(chuàng)建的 class 對(duì)象是我們修改之后的字節(jié)碼內(nèi)容吃度,那就可以實(shí)現(xiàn)?個(gè)回調(diào)函數(shù)賦給 jvmtiEnv(JVMTI 的運(yùn)
?時(shí),通常?個(gè) JVMTIAgent 對(duì)應(yīng)?個(gè) jvmtiEnv贴硫,但是也可以對(duì)應(yīng)多個(gè))的回調(diào)?法集合?的 ClassFileLoadHook椿每,這樣在接下來(lái)的類(lèi)?件加
載過(guò)程中都會(huì)調(diào)?到這個(gè)函數(shù)中。
instrument agent
instrument agent 實(shí)現(xiàn)了Agent_OnLoad和Agent_OnAttach兩?法英遭,也就是說(shuō)在使?時(shí)间护,agent既可以在啟動(dòng)時(shí)加載,也可以在運(yùn)?時(shí)動(dòng)態(tài)加
載挖诸。其中啟動(dòng)時(shí)加載還可以通過(guò)類(lèi)似-javaagent:myagent.jar的?式來(lái)間接加載 instrument agent汁尺,運(yùn)?時(shí)動(dòng)態(tài)加載依賴(lài)的是 JVM 的 attach 機(jī)制
( JVM Attach 機(jī)制實(shí)現(xiàn)),通過(guò)發(fā)送 load 命令來(lái)加載 agent多律。
在啟動(dòng)時(shí)加載 instrument agent
啟動(dòng)時(shí)加載 instrument agent均函,具體過(guò)程都在InvocationAdapter.c
的Agent_OnLoad
?法?,這?簡(jiǎn)單描述下過(guò)程:
創(chuàng)建并初始化 JPLISAgent
監(jiān)聽(tīng) VMInit 事件菱涤,在 vm 初始化完成之后做下?的事情:
創(chuàng)建 InstrumentationImpl 對(duì)象
監(jiān)聽(tīng) ClassFileLoadHook 事件
調(diào)? InstrumentationImpl 的loadClassAndCallPremain
?法苞也,在這個(gè)?法?會(huì)調(diào)? javaage
在運(yùn)?時(shí)加載 instrument agent
上?會(huì)通過(guò) JVM 的 attach 機(jī)制來(lái)請(qǐng)求?標(biāo) JVM 加載對(duì)應(yīng)的 agent,過(guò)程?致如下:
創(chuàng)建并初始化 JPLISAgent
解析 javaagent ? MANIFEST.MF ?的參數(shù)
創(chuàng)建 InstrumentationImpl 對(duì)象
監(jiān)聽(tīng) ClassFileLoadHook 事件
調(diào)? InstrumentationImpl 的loadClassAndCallAgentmain?法粘秆,在這個(gè)?法?會(huì)調(diào)? javaagent ? MANIFEST.MF ?指定的Agent-Class類(lèi)的
agentmain?法
Zipkin
Zipkin 組件設(shè)計(jì)
Zipkin是?個(gè)分布式追蹤系統(tǒng)如迟。它有助于收集解決微服務(wù)架構(gòu)中延遲
問(wèn)題所需的時(shí)序數(shù)據(jù)。它管理這些數(shù)據(jù)的收集和查找攻走。Zipkin的設(shè)計(jì)
基于 Google Dapper論?殷勘。
共有四個(gè)組件構(gòu)成了 Zipkin:
- collector
- storage
- search
- web UI
Reporter是裝配應(yīng)?中?于向 Zipkin 發(fā)送數(shù)據(jù)的組件。Reporter 通過(guò)
Transport 發(fā)送追蹤數(shù)據(jù)到 Zipkin 的 Collector昔搂,Collector 持久化數(shù)據(jù)
到 Storage 中玲销。之后,API 從 Storage 中查詢數(shù)據(jù)提供給 UI摘符。
Zipkin 架構(gòu)設(shè)計(jì)
Transport:
裝配庫(kù)發(fā)送的跨度必須由裝配的服務(wù)傳輸?shù)?Collector贤斜。 有三種主要的傳輸類(lèi)型:
HTTP、Scribe 和 Kafka逛裤。更多信息查看跨度接收器瘩绒。Collector:
?旦追蹤數(shù)據(jù)抵達(dá) Zipkin Collector 守護(hù)進(jìn)程,Zipkin Collector 為了查詢带族,會(huì)對(duì)其進(jìn)
?校驗(yàn)锁荔、存儲(chǔ)和索引。Storage:
Zipkin 最初是構(gòu)建在將數(shù)據(jù)存儲(chǔ)在 Cassandra 中蝙砌,因?yàn)?Cassandra 易跨站阳堕,?持
靈活的 schema跋理,并且在 Twitter 內(nèi)部被?規(guī)模使?。然?恬总,我們將這個(gè)組件做成了
可插拔式的薪介。在 Cassandra 之外,我們?cè)??持 ElasticSearch 和 MySQL越驻。可作為
第三?擴(kuò)展提供給其它后端道偷。
Zipkin 查詢服務(wù):
?旦數(shù)據(jù)被存儲(chǔ)索引缀旁,我們就需要?種?式提取它。查詢守護(hù)進(jìn)程提供了?個(gè)簡(jiǎn)單
的 JSON API 查詢和獲取追蹤數(shù)據(jù)勺鸦。API 的主要消費(fèi)者就是 Web UI并巍。
Web UI:
我們創(chuàng)建了?個(gè)?戶圖形界?為追蹤數(shù)據(jù)提供了?個(gè)漂亮的視圖。Web UI 提供了基
于服務(wù)换途、時(shí)間和標(biāo)記(annotation)查看追中數(shù)據(jù)的?法懊渡。注意:UI 沒(méi)有內(nèi)置的身
份認(rèn)證功能。
總結(jié)
- 非侵入性军拟,并且對(duì) JAVA, .NET 等語(yǔ)言支持剃执,可以選擇 SkyWalking, 接入成本較小 ;
- 如果采用 OpenTracing 集成接入懈息,可以選擇 Jaeger 或者 Zipkin 肾档,侵入性比較大,接入成本中等辫继;
- Service Mesh Tracing 接入 (如 istio tracing )怒见,侵入性較小,接入成本較小 姑宽;
在生產(chǎn)中使用遣耍,需要看團(tuán)隊(duì)研發(fā)力量和需求,1-3 種方案均可以實(shí)施炮车。
下一章和大家分享 自研 分布式鏈路追蹤 SDK 舵变。
- By 斜桿青年