主流微服務(wù)全鏈路監(jiān)控系統(tǒng)之戰(zhàn)

問題背景
隨著微服務(wù)架構(gòu)的流行,服務(wù)按照不同的維度進(jìn)行拆分,一次請(qǐng)求往往需要涉及到多個(gè)服務(wù)。
互聯(lián)網(wǎng)應(yīng)用構(gòu)建在不同的軟件模塊集上,這些軟件模塊,有可能是由不同的團(tuán)隊(duì)開發(fā)、可能使用不同的編程語言來實(shí)現(xiàn)、有可能布在了幾千臺(tái)服務(wù)器,橫跨多個(gè)不同的數(shù)據(jù)中心。
因此,就需要一些可以幫助理解系統(tǒng)行為、用于分析性能問題的工具,以便發(fā)生故障的時(shí)候,能夠快速定位和解決問題。
全鏈路監(jiān)控組件就在這樣的問題背景下產(chǎn)生了。最出名的是谷歌公開的論文提到的 Google Dapper。
想要在這個(gè)上下文中理解分布式系統(tǒng)的行為,就需要監(jiān)控那些橫跨了不同的應(yīng)用、不同的服務(wù)器之間的關(guān)聯(lián)動(dòng)作。
所以,在復(fù)雜的微服務(wù)架構(gòu)系統(tǒng)中,幾乎每一個(gè)前端請(qǐng)求都會(huì)形成一個(gè)復(fù)雜的分布式服務(wù)調(diào)用鏈路。一個(gè)請(qǐng)求完整調(diào)用鏈可能如下圖所示:

如何快速發(fā)現(xiàn)問題? 如何判斷故障影響范圍? 如何梳理服務(wù)依賴以及依賴的合理性? 如何分析鏈路性能問題以及實(shí)時(shí)容量規(guī)劃?
吞吐量,根據(jù)拓?fù)淇捎?jì)算相應(yīng)組件、平臺(tái)、物理設(shè)備的實(shí)時(shí)吞吐量。
響應(yīng)時(shí)間,包括整體調(diào)用的響應(yīng)時(shí)間和各個(gè)服務(wù)的響應(yīng)時(shí)間等。
錯(cuò)誤記錄,根據(jù)服務(wù)返回統(tǒng)計(jì)單位時(shí)間異常次數(shù)。
請(qǐng)求鏈路追蹤,故障快速定位:可以通過調(diào)用鏈結(jié)合業(yè)務(wù)日志快速定位錯(cuò)誤信息。 可視化:各個(gè)階段耗時(shí),進(jìn)行性能分析。 依賴優(yōu)化:各個(gè)調(diào)用環(huán)節(jié)的可用性、梳理服務(wù)依賴關(guān)系以及優(yōu)化。 數(shù)據(jù)分析,優(yōu)化鏈路:可以得到用戶的行為路徑,匯總分析應(yīng)用在很多業(yè)務(wù)場(chǎng)景。
1 目標(biāo)要求
1、探針的性能消耗
APM 組件服務(wù)的影響應(yīng)該做到足夠小。服務(wù)調(diào)用埋點(diǎn)本身會(huì)帶來性能損耗,這就需要調(diào)用跟蹤的低損耗,實(shí)際中還會(huì)通過配置采樣率的方式,選擇一部分請(qǐng)求去分析請(qǐng)求路徑。在一些高度優(yōu)化過的服務(wù),即使一點(diǎn)點(diǎn)損耗也會(huì)很容易察覺到,而且有可能迫使在線服務(wù)的部署團(tuán)隊(duì)不得不將跟蹤系統(tǒng)關(guān)停。
2、代碼的侵入性
即也作為業(yè)務(wù)組件,應(yīng)當(dāng)盡可能少入侵或者無入侵其他業(yè)務(wù)系統(tǒng),對(duì)于使用方透明,減少開發(fā)人員的負(fù)擔(dān)。
對(duì)于應(yīng)用的程序員來說,是不需要知道有跟蹤系統(tǒng)這回事的。如果一個(gè)跟蹤系統(tǒng)想生效,就必須需要依賴應(yīng)用的開發(fā)者主動(dòng)配合,那么這個(gè)跟蹤系統(tǒng)也太脆弱了,往往由于跟蹤系統(tǒng)在應(yīng)用中植入代碼的 bug 或疏忽導(dǎo)致應(yīng)用出問題,這樣才是無法滿足對(duì)跟蹤系統(tǒng)“無所不在的部署”這個(gè)需求。
3、可擴(kuò)展性
4、數(shù)據(jù)的分析
2 功能模塊
一般的全鏈路監(jiān)控系統(tǒng),大致可分為四大功能模塊:
1.埋點(diǎn)與生成日志
埋點(diǎn)即系統(tǒng)在當(dāng)前節(jié)點(diǎn)的上下文信息,可以分為 客戶端埋點(diǎn)、服務(wù)端埋點(diǎn),以及客戶端和服務(wù)端雙向型埋點(diǎn)。埋點(diǎn)日志通常要包含以下內(nèi)容traceId、spanId、調(diào)用的開始時(shí)間,協(xié)議類型、調(diào)用方ip和端口,請(qǐng)求的服務(wù)名、調(diào)用耗時(shí),調(diào)用結(jié)果,異常信息等,同時(shí)預(yù)留可擴(kuò)展字段,為下一步擴(kuò)展做準(zhǔn)備;
不能造成性能負(fù)擔(dān):一個(gè)價(jià)值未被驗(yàn)證,卻會(huì)影響性能的東西,是很難在公司推廣的! 因?yàn)橐獙?log,業(yè)務(wù) QPS 越高,性能影響越重。通過采樣和異步log解決。
2.收集和存儲(chǔ)日志
每個(gè)機(jī)器上有一個(gè) deamon 做日志收集,業(yè)務(wù)進(jìn)程把自己的 Trace 發(fā)到 daemon,daemon 把收集 Trace 往上一級(jí)發(fā)送;
多級(jí)的 collector,類似 pub/sub 架構(gòu),可以負(fù)載均衡;
對(duì)聚合的數(shù)據(jù)進(jìn)行 實(shí)時(shí)分析和離線存儲(chǔ);
離線分析 需要將同一條調(diào)用鏈的日志匯總在一起;
3.分析和統(tǒng)計(jì)調(diào)用鏈路數(shù)據(jù),以及時(shí)效性
調(diào)用鏈跟蹤分析:把同一 TraceID 的 Span 收集起來,按時(shí)間排序就是timeline。把ParentID 串起來就是調(diào)用棧。
拋異?;蛘叱瑫r(shí),在日志里打印 TraceID。利用 TraceID 查詢調(diào)用鏈情況,定位問題。
強(qiáng)依賴:調(diào)用失敗會(huì)直接中斷主流程
高度依賴:一次鏈路中調(diào)用某個(gè)依賴的幾率高
頻繁依賴:一次鏈路調(diào)用同一個(gè)依賴的次數(shù)多
離線分析:按TraceID匯總,通過Span的ID和ParentID還原調(diào)用關(guān)系,分析鏈路形態(tài)。
實(shí)時(shí)分析:對(duì)單條日志直接分析,不做匯總,重組。得到當(dāng)前QPS,延遲。
4.展現(xiàn)以及決策支持
3 Google Dapper
3.1 Span
基本工作單元,一次鏈路調(diào)用(可以是RPC,DB等沒有特定的限制)創(chuàng)建一個(gè)span,通過一個(gè)64位ID標(biāo)識(shí)它,uuid較為方便,span中還有其他的數(shù)據(jù),例如描述信息,時(shí)間戳,key-value對(duì)的(Annotation)tag信息,parent_id等,其中parent-id可以表示span調(diào)用鏈路來源。

上圖說明了 span 在一次大的跟蹤過程中是什么樣的。Dapper 記錄了 span 名稱,以及每個(gè) span 的 ID 和父 ID,以重建在一次追蹤過程中不同 span 之間的關(guān)系。如果一個(gè) span沒有父 ID 被稱為 root span。所有 span 都掛在一個(gè)特定的跟蹤上,也共用一個(gè)跟蹤 id。
Span 數(shù)據(jù)結(jié)構(gòu):
type Span struct {TraceIDint64 // 用于標(biāo)示一次完整的請(qǐng)求idName stringID int64 // 當(dāng)前這次調(diào)用span_idParentID int64 // 上層服務(wù)的調(diào)用span_id最上層服務(wù)parent_id為nullAnnotation []Annotation // 用于標(biāo)記的時(shí)間戳Debugbool}
3.2 Trace
類似于 樹結(jié)構(gòu)的Span集合,表示一次完整的跟蹤,從請(qǐng)求到服務(wù)器開始,服務(wù)器返回response結(jié)束,跟蹤每次rpc調(diào)用的耗時(shí),存在唯一標(biāo)識(shí)trace_id。比如:你運(yùn)行的分布式大數(shù)據(jù)存儲(chǔ)一次Trace就由你的一次請(qǐng)求組成。

每種顏色的note標(biāo)注了一個(gè)span,一條鏈路通過TraceId唯一標(biāo)識(shí),Span標(biāo)識(shí)發(fā)起的請(qǐng)求信息。樹節(jié)點(diǎn)是整個(gè)架構(gòu)的基本單元,而每一個(gè)節(jié)點(diǎn)又是對(duì)span的引用。節(jié)點(diǎn)之間的連線表示的span和它的父span直接的關(guān)系。雖然span在日志文件中只是簡(jiǎn)單的代表span的開始和結(jié)束時(shí)間,他們?cè)谡麄€(gè)樹形結(jié)構(gòu)中卻是相對(duì)獨(dú)立的。
3.3 Annotation
注解,用來記錄請(qǐng)求特定事件相關(guān)信息(例如時(shí)間),一個(gè)span中會(huì)有多個(gè)annotation注解描述。通常包含四個(gè)注解信息:
(1) cs:Client Start,表示客戶端發(fā)起請(qǐng)求
(2) sr:Server Receive,表示服務(wù)端收到請(qǐng)求
(3) ss:Server Send,表示服務(wù)端完成處理,并將結(jié)果發(fā)送給客戶端
(4) cr:Client Received,表示客戶端獲取到服務(wù)端返回信息
Annotation數(shù)據(jù)結(jié)構(gòu):
type Annotation struct {Timestamp int64Value stringHostEndpointDurationint32}
3.4 調(diào)用示例
1. 請(qǐng)求調(diào)用示例
當(dāng)用戶發(fā)起一個(gè)請(qǐng)求時(shí),首先到達(dá)前端A服務(wù),然后分別對(duì)B服務(wù)和C服務(wù)進(jìn)行RPC調(diào)用;
B服務(wù)處理完給A做出響應(yīng),但是C服務(wù)還需要和后端的D服務(wù)和E服務(wù)交互之后再返還給A服務(wù),最后由A服務(wù)來響應(yīng)用戶的請(qǐng)求;

2. 調(diào)用過程追蹤
請(qǐng)求到來生成一個(gè)全局 TraceID,通過 TraceID 可以串聯(lián)起整個(gè)調(diào)用鏈,一個(gè)TraceID 代表一次請(qǐng)求。 除了TraceID外,還需要SpanID用于記錄調(diào)用父子關(guān)系。每個(gè)服務(wù)會(huì)記錄下parent id和span id,通過他們可以組織一次完整調(diào)用鏈的父子關(guān)系。 一個(gè)沒有parent id的span成為root span,可以看成調(diào)用鏈入口。 所有這些ID可用全局唯一的64位整數(shù)表示; 整個(gè)調(diào)用過程中每個(gè)請(qǐng)求都要透?jìng)鱐raceID和SpanID。 每個(gè)服務(wù)將該次請(qǐng)求附帶的TraceID和附帶的SpanID作為parent id記錄下,并且將自己生成的SpanID也記錄下。 要查看某次完整的調(diào)用則 只要根據(jù)TraceID查出所有調(diào)用記錄,然后通過parent id和span id組織起整個(gè)調(diào)用父子關(guān)系。

3. 調(diào)用鏈核心工作
調(diào)用鏈數(shù)據(jù)生成,對(duì)整個(gè)調(diào)用過程的所有應(yīng)用進(jìn)行埋點(diǎn)并輸出日志。
調(diào)用鏈數(shù)據(jù)采集,對(duì)各個(gè)應(yīng)用中的日志數(shù)據(jù)進(jìn)行采集。
調(diào)用鏈數(shù)據(jù)存儲(chǔ)及查詢,對(duì)采集到的數(shù)據(jù)進(jìn)行存儲(chǔ),由于日志數(shù)據(jù)量一般都很大,不僅要能對(duì)其存儲(chǔ),還需要能提供快速查詢。
指標(biāo)運(yùn)算、存儲(chǔ)及查詢,對(duì)采集到的日志數(shù)據(jù)進(jìn)行各種指標(biāo)運(yùn)算,將運(yùn)算結(jié)果保存起來。
告警功能,提供各種閥值警告功能。
4. 整體部署架構(gòu)

5. AGENT無侵入部署
通過AGENT代理無侵入式部署,將性能測(cè)量與業(yè)務(wù)邏輯完全分離,可以測(cè)量任意類的任意方法的執(zhí)行時(shí)間,這種方式大大提高了采集效率,并且減少運(yùn)維成本。根據(jù)服務(wù)跨度主要分為兩大類AGENT:
服務(wù)內(nèi)AGENT,這種方式是通過 Java 的agent機(jī)制,對(duì)服務(wù)內(nèi)部的方法調(diào)用層次信息進(jìn)行數(shù)據(jù)收集,如方法調(diào)用耗時(shí)、入?yún)?、出參等信息?/span>
跨服務(wù)AGENT,這種情況需要對(duì)主流RPC框架以插件形式提供無縫支持。并通過提供標(biāo)準(zhǔn)數(shù)據(jù)規(guī)范以適應(yīng)自定義RPC框架:
(1)Dubbo支持;(2)Rest支持;(3)自定義RPC支持;
6. 調(diào)用鏈監(jiān)控好處
準(zhǔn)確掌握生產(chǎn)一線應(yīng)用部署情況;
從調(diào)用鏈全流程性能角度,識(shí)別對(duì)關(guān)鍵調(diào)用鏈,并進(jìn)行優(yōu)化;
提供可追溯的性能數(shù)據(jù),量化 IT 運(yùn)維部門業(yè)務(wù)價(jià)值;
快速定位代碼性能問題,協(xié)助開發(fā)人員持續(xù)性的優(yōu)化代碼;
協(xié)助開發(fā)人員進(jìn)行白盒測(cè)試,縮短系統(tǒng)上線穩(wěn)定期;
4 方案比較
Zipkin:由Twitter公司開源,開放源代碼分布式的跟蹤系統(tǒng),用于收集服務(wù)的定時(shí)數(shù)據(jù),以解決微服務(wù)架構(gòu)中的延遲問題,包括:數(shù)據(jù)的收集、存儲(chǔ)、查找和展現(xiàn)。 Pinpoint:一款對(duì)Java編寫的大規(guī)模分布式系統(tǒng)的APM工具,由韓國人開源的分布式跟蹤組件。 Skywalking:國產(chǎn)的優(yōu)秀APM組件,是一個(gè)對(duì)JAVA分布式應(yīng)用程序集群的業(yè)務(wù)運(yùn)行情況進(jìn)行追蹤、告警和分析的系統(tǒng)。
1.探針的性能
主要是agent對(duì)服務(wù)的吞吐量、CPU和內(nèi)存的影響。微服務(wù)的規(guī)模和動(dòng)態(tài)性使得數(shù)據(jù)收集的成本大幅度提高。
2.collector的可擴(kuò)展性
能夠水平擴(kuò)展以便支持大規(guī)模服務(wù)器集群。
3.全面的調(diào)用鏈路數(shù)據(jù)分析
提供代碼級(jí)別的可見性以便輕松定位失敗點(diǎn)和瓶頸。
4.對(duì)于開發(fā)透明,容易開關(guān)
添加新功能而無需修改代碼,容易啟用或者禁用。
5.完整的調(diào)用鏈應(yīng)用拓?fù)?br/>自動(dòng)檢測(cè)應(yīng)用拓?fù)洌瑤椭愀闱宄?yīng)用的架構(gòu)
4.1 探針的性能

從上表可以看出,在三種鏈路監(jiān)控組件中,skywalking 的探針對(duì)吞吐量的影響最小,zipkin的吞吐量居中。pinpoint 的探針對(duì)吞吐量的影響較為明顯,在500并發(fā)用戶時(shí),測(cè)試服務(wù)的吞吐量從1385降低到774,影響很大。然后再看下CPU和memory的影響,在內(nèi)部服務(wù)器進(jìn)行的壓測(cè),對(duì)CPU和memory的影響都差不多在10%之內(nèi)。
4.2 collector的可擴(kuò)展性
1. zipkin
開發(fā) zipkin-Server(其實(shí)就是提供的開箱即用包),zipkin-agent 與 zipkin-Server 通過 http 或者 mq 進(jìn)行通信,http 通信會(huì)對(duì)正常的訪問造成影響,所以還是推薦基于mq異步方式通信,zipkin-Server 通過訂閱具體的 topic 進(jìn)行消費(fèi)。這個(gè)當(dāng)然是可以擴(kuò)展的,多個(gè) zipkin-Server 實(shí)例進(jìn)行異步消費(fèi) mq 中的監(jiān)控信息。

4.3 全面的調(diào)用鏈路數(shù)據(jù)分析
全面的調(diào)用鏈路數(shù)據(jù)分析,提供代碼級(jí)別的可見性以便輕松定位失敗點(diǎn)和瓶頸。

zipkin鏈路調(diào)用分析

skywalking鏈路調(diào)用分析

pinpoint鏈路調(diào)用分析
4.4 對(duì)于開發(fā)透明,容易開關(guān)
對(duì)于開發(fā)透明,容易開關(guān),添加新功能而無需修改代碼,容易啟用或者禁用。我們期望功能可以不修改代碼就工作并希望得到代碼級(jí)別的可見性。
對(duì)于這一點(diǎn),Zipkin 使用修改過的類庫和它自己的容器(Finagle)來提供分布式事務(wù)跟蹤的功能。但是,它要求在需要時(shí)修改代碼。skywalking和pinpoint都是基于字節(jié)碼增強(qiáng)的方式,開發(fā)人員不需要修改代碼,并且可以收集到更多精確的數(shù)據(jù)因?yàn)橛凶止?jié)碼中的更多信息。
4.5 完整的調(diào)用鏈應(yīng)用拓?fù)?/h3>
自動(dòng)檢測(cè)應(yīng)用拓?fù)洌瑤椭愀闱宄?yīng)用的架構(gòu)。



上面三幅圖,分別展示了APM 組件各自的調(diào)用拓?fù)?,都能?shí)現(xiàn)完整的調(diào)用鏈應(yīng)用拓?fù)?。相?duì)來說,pinpoint 界面顯示的更加豐富,具體到調(diào)用的DB名,zipkin的拓?fù)渚窒抻诜?wù)于服務(wù)之間。
4.6 Pinpoint與Zipkin細(xì)化比較
4.6.1 Pinpoint與Zipkin差異性
Pinpoint 是一個(gè)完整的性能監(jiān)控解決方案:有從探針、收集器、存儲(chǔ)到 Web 界面等全套體系;而 Zipkin 只側(cè)重收集器和存儲(chǔ)服務(wù),雖然也有用戶界面,但其功能與 Pinpoint 不可同日而語。反而 Zipkin 提供有 Query 接口,更強(qiáng)大的用戶界面和系統(tǒng)集成能力,可以基于該接口二次開發(fā)實(shí)現(xiàn)。
Zipkin 官方提供有基于 Finagle 框架(Scala 語言)的接口,而其他框架的接口由社區(qū)貢獻(xiàn),目前可以支持 Java、Scala、Node、Go、Python、Ruby 和 C# 等主流開發(fā)語言和框架;但是 Pinpoint 目前只有官方提供的 Java Agent 探針,其他的都在請(qǐng)求社區(qū)支援中(請(qǐng)參見 #1759 和 #1760)。
Pinpoint 提供有 Java Agent 探針,通過字節(jié)碼注入的方式實(shí)現(xiàn)調(diào)用攔截和數(shù)據(jù)收集,可以做到真正的代碼無侵入,只需要在啟動(dòng)服務(wù)器的時(shí)候添加一些參數(shù),就可以完成探針的部署;而 Zipkin 的 Java 接口實(shí)現(xiàn) Brave,只提供了基本的操作 API,如果需要與框架或者項(xiàng)目集成的話,就需要手動(dòng)添加配置文件或增加代碼。
Pinpoint 的后端存儲(chǔ)基于 HBase,而 Zipkin 基于 Cassandra。
4.6.2 Pinpoint 與 Zipkin 相似性
Pinpoint 與 Zipkin 都是基于 Google Dapper 的那篇論文,因此理論基礎(chǔ)大致相同。兩者都是將服務(wù)調(diào)用拆分成若干有級(jí)聯(lián)關(guān)系的 Span,通過 SpanId 和 ParentSpanId 來進(jìn)行調(diào)用關(guān)系的級(jí)聯(lián);最后再將整個(gè)調(diào)用鏈流經(jīng)的所有的 Span 匯聚成一個(gè) Trace,報(bào)告給服務(wù)端的 collector 進(jìn)行收集和存儲(chǔ)。
即便在這一點(diǎn)上,Pinpoint 所采用的概念也不完全與那篇論文一致。比如他采用 TransactionId 來取代 TraceId,而真正的 TraceId 是一個(gè)結(jié)構(gòu),里面包含了 TransactionId, SpanId 和 ParentSpanId。而且 Pinpoint 在 Span 下面又增加了一個(gè) SpanEvent 結(jié)構(gòu),用來記錄一個(gè) Span 內(nèi)部的調(diào)用細(xì)節(jié)(比如具體的方法調(diào)用等等),因此 Pinpoint 默認(rèn)會(huì)比 Zipkin 記錄更多的跟蹤數(shù)據(jù)。
但是理論上并沒有限定 Span 的粒度大小,所以一個(gè)服務(wù)調(diào)用可以是一個(gè) Span,那么每個(gè)服務(wù)中的方法調(diào)用也可以是個(gè) Span,這樣的話,其實(shí) Brave 也可以跟蹤到方法調(diào)用級(jí)別,只是具體實(shí)現(xiàn)并沒有這樣做而已。
4.6.3 字節(jié)碼注入 vs API 調(diào)用
Pinpoint 實(shí)現(xiàn)了基于字節(jié)碼注入的 Java Agent 探針,而 Zipkin 的 Brave 框架僅僅提供了應(yīng)用層面的 API,但是細(xì)想問題遠(yuǎn)不那么簡(jiǎn)單。字節(jié)碼注入是一種簡(jiǎn)單粗暴的解決方案,理論上來說無論任何方法調(diào)用,都可以通過注入代碼的方式實(shí)現(xiàn)攔截,也就是說沒有實(shí)現(xiàn)不了的,只有不會(huì)實(shí)現(xiàn)的。但 Brave 則不同,其提供的應(yīng)用層面的 API 還需要框架底層驅(qū)動(dòng)的支持,才能實(shí)現(xiàn)攔截。
比如,MySQL 的 JDBC 驅(qū)動(dòng),就提供有注入 interceptor 的方法,因此只需要實(shí)現(xiàn) StatementInterceptor 接口,并在 Connection String 中進(jìn)行配置,就可以很簡(jiǎn)單的實(shí)現(xiàn)相關(guān)攔截;而與此相對(duì)的,低版本的 MongoDB 的驅(qū)動(dòng)或者是 Spring Data MongoDB 的實(shí)現(xiàn)就沒有如此接口,想要實(shí)現(xiàn)攔截查詢語句的功能,就比較困難。
因此在這一點(diǎn)上,Brave 是硬傷,無論使用字節(jié)碼注入多么困難,但至少也是可以實(shí)現(xiàn)的,但是 Brave 卻有無從下手的可能,而且是否可以注入,能夠多大程度上注入,更多的取決于框架的 API 而不是自身的能力。
4.6.4 難度及成本
經(jīng)過簡(jiǎn)單閱讀 Pinpoint 和 Brave 插件的代碼,可以發(fā)現(xiàn)兩者的實(shí)現(xiàn)難度有天壤之別。在都沒有任何開發(fā)文檔支撐的前提下,Brave 比 Pinpoint 更容易上手。Brave 的代碼量很少,核心功能都集中在 brave-core 這個(gè)模塊下,一個(gè)中等水平的開發(fā)人員,可以在一天之內(nèi)讀懂其內(nèi)容,并且能對(duì) API 的結(jié)構(gòu)有非常清晰的認(rèn)識(shí)。
Pinpoint 的代碼封裝也是非常好的,尤其是針對(duì)字節(jié)碼注入的上層 API 的封裝非常出色,但是這依然要求閱讀人員對(duì)字節(jié)碼注入多少有一些了解,雖然其用于注入代碼的核心 API 并不多,但要想了解透徹,恐怕還得深入 Agent 的相關(guān)代碼,比如很難一目了然的理解 addInterceptor 和 addScopedInterceptor 的區(qū)別,而這兩個(gè)方法就是位于 Agent 的有關(guān)類型中。
因?yàn)?Brave 的注入需要依賴底層框架提供相關(guān)接口,因此并不需要對(duì)框架有一個(gè)全面的了解,只需要知道能在什么地方注入,能夠在注入的時(shí)候取得什么數(shù)據(jù)就可以了。就像上面的例子,我們根本不需要知道 MySQL 的 JDBC Driver 是如何實(shí)現(xiàn)的也可以做到攔截 SQL 的能力。但是 Pinpoint 就不然,因?yàn)?Pinpoint 幾乎可以在任何地方注入任何代碼,這需要開發(fā)人員對(duì)所需注入的庫的代碼實(shí)現(xiàn)有非常深入的了解,通過查看其 MySQL 和 Http Client 插件的實(shí)現(xiàn)就可以洞察這一點(diǎn),當(dāng)然這也從另外一個(gè)層面說明 Pinpoint 的能力確實(shí)可以非常強(qiáng)大,而且其默認(rèn)實(shí)現(xiàn)的很多插件已經(jīng)做到了非常細(xì)粒度的攔截。
針對(duì)底層框架沒有公開 API 的時(shí)候,其實(shí) Brave 也并不完全無計(jì)可施,我們可以采取 AOP 的方式,一樣能夠?qū)⑾嚓P(guān)攔截注入到指定的代碼中,而且顯然 AOP 的應(yīng)用要比字節(jié)碼注入簡(jiǎn)單很多。
以上這些直接關(guān)系到實(shí)現(xiàn)一個(gè)監(jiān)控的成本,在 Pinpoint 的官方技術(shù)文檔中,給出了一個(gè)參考數(shù)據(jù)。如果對(duì)一個(gè)系統(tǒng)集成的話,那么用于開發(fā) Pinpoint 插件的成本是 100,將此插件集成入系統(tǒng)的成本是 0;但對(duì)于 Brave,插件開發(fā)的成本只有 20,而集成成本是 10。從這一點(diǎn)上可以看出官方給出的成本參考數(shù)據(jù)是 5:1。但是官方又強(qiáng)調(diào)了,如果有 10 個(gè)系統(tǒng)需要集成的話,那么總成本就是 10 * 10 + 20 = 120,就超出了 Pinpoint 的開發(fā)成本 100,而且需要集成的服務(wù)越多,這個(gè)差距就越大。
4.6.5 通用性和擴(kuò)展性
很顯然,這一點(diǎn)上 Pinpoint 完全處于劣勢(shì),從社區(qū)所開發(fā)出來的集成接口就可見一斑。
Pinpoint 的數(shù)據(jù)接口缺乏文檔,而且也不太標(biāo)準(zhǔn)(參考論壇討論帖),需要閱讀很多代碼才可能實(shí)現(xiàn)一個(gè)自己的探針(比如 Node 的或者 PHP 的)。而且團(tuán)隊(duì)為了性能考慮使用了 Thrift 作為數(shù)據(jù)傳輸協(xié)議標(biāo)準(zhǔn),比起 HTTP 和 JSON 而言難度增加了不少。
4.6.6 社區(qū)支持
這一點(diǎn)也不必多說,Zipkin 由 Twitter 開發(fā),可以算得上是明星團(tuán)隊(duì),而 Naver 的團(tuán)隊(duì)只是一個(gè)默默無聞的小團(tuán)隊(duì)(從 #1759 的討論中可以看出)。雖然說這個(gè)項(xiàng)目在短期內(nèi)不太可能消失或停止更新,但畢竟不如前者用起來更加放心。而且沒有更多社區(qū)開發(fā)出來的插件,讓 Pinpoint 只依靠團(tuán)隊(duì)自身的力量完成諸多框架的集成實(shí)屬困難,而且他們目前的工作重點(diǎn)依然是在提升性能和穩(wěn)定性上。