首先,我們了解一下移動(dòng)端全鏈路 Trace 的背景:
從移動(dòng)端的視角來(lái)看,一個(gè) App 產(chǎn)品從概念產(chǎn)生,到最終的成熟穩(wěn)定,產(chǎn)品研發(fā)過(guò)程中涉及到的研發(fā)人員、工程中的代碼行數(shù)、工程架構(gòu)規(guī)模、產(chǎn)品發(fā)布頻率、線上業(yè)務(wù)問(wèn)題修復(fù)時(shí)間等等都會(huì)發(fā)生比較大的變化。這些變化,給我們?cè)谂挪閱?wèn)題方面帶來(lái)不小的困難和挑戰(zhàn),業(yè)務(wù)問(wèn)題會(huì)往往難以復(fù)現(xiàn)和排查定位。
比如,在產(chǎn)品初期的時(shí)候,工程規(guī)模往往比較小,業(yè)務(wù)流程也比較簡(jiǎn)單,線上問(wèn)題往往能很快定位。而等到工程規(guī)模比較大的時(shí)候,業(yè)務(wù)流程往往涉及到的模塊會(huì)比較多,這個(gè)時(shí)候有些線上問(wèn)題就會(huì)比較難以復(fù)現(xiàn)和定位排查。
本文匯集了筆者在 2022 D2 終端技術(shù)大會(huì)上的相關(guān)技術(shù)分享,希望能給大家?guī)?lái)一些思考和啟發(fā)。
端側(cè)問(wèn)題為什么很難復(fù)現(xiàn)和定位?
線上業(yè)務(wù)問(wèn)題為什么很難復(fù)現(xiàn)和排查定位?經(jīng)過(guò)我們的分析,主要是由 4 個(gè)原因?qū)е拢?/p>
移動(dòng)端 & 服務(wù)端日志采集不統(tǒng)一,沒(méi)有統(tǒng)一的標(biāo)準(zhǔn)規(guī)范來(lái)約束數(shù)據(jù)的采集和處理。
端側(cè)往往涉及的模塊非常多,研發(fā)框架也各不相同,代碼相互隔離,設(shè)備碎片化,網(wǎng)絡(luò)環(huán)境復(fù)雜,會(huì)導(dǎo)致端側(cè)數(shù)據(jù)采集比較難。
從端視角出發(fā),不同框架、系統(tǒng)之間的數(shù)據(jù)在分析問(wèn)題時(shí)往往獲取比較難,而且數(shù)據(jù)之間缺少上下文關(guān)聯(lián)信息,數(shù)據(jù)關(guān)聯(lián)分析不容易。
業(yè)務(wù)鏈路涉及到的業(yè)務(wù)域往往也會(huì)比較多,從端的視角去復(fù)現(xiàn)和排查問(wèn)題,往往需要對(duì)應(yīng)域的同學(xué)參與排查,人肉運(yùn)維成本比較高。
這些問(wèn)題如何來(lái)解決? 我們的思路是四步走:
建立統(tǒng)一標(biāo)準(zhǔn),使用 標(biāo)準(zhǔn)協(xié)議 來(lái)約束數(shù)據(jù)的采集和處理。
針對(duì)不同的平臺(tái)和框架,統(tǒng)一數(shù)據(jù)采集能力。
對(duì)多系統(tǒng)、多模塊產(chǎn)生的數(shù)據(jù)進(jìn)行自動(dòng)上下文關(guān)聯(lián)分析和處理。
我們也基于機(jī)器學(xué)習(xí),在自動(dòng)化經(jīng)驗(yàn)分析方面做了一些探索。
統(tǒng)一數(shù)據(jù)采集標(biāo)準(zhǔn)
如何統(tǒng)一標(biāo)準(zhǔn)? 目前行業(yè)內(nèi)也有各種各樣的解決方案,但存在的問(wèn)題也很明顯:
不同方案之間,協(xié)議/數(shù)據(jù)類(lèi)型不統(tǒng)一;
不同方案之間,也比較難以兼容/互通。
標(biāo)準(zhǔn)這里,我們選擇了 OTel,OTel 是 OpenTelemetry 的簡(jiǎn)稱(chēng),主要原因有兩點(diǎn):
OTel 是由云原生計(jì)算基金會(huì)(CNCF)主導(dǎo),它是由 OpenTracing 和 OpenCensus 合并而來(lái),是目前可觀測(cè)性領(lǐng)域的準(zhǔn)標(biāo)準(zhǔn)協(xié)議;
OTel 對(duì)不同語(yǔ)言和數(shù)據(jù)模型進(jìn)行了統(tǒng)一,可以同時(shí)兼容 OpenTracing 和 OpenCensus,它還提供了一個(gè)廠商無(wú)關(guān)的 Collectors,用于接收、處理和導(dǎo)出可觀測(cè)數(shù)據(jù)。
在我們的解決方案中,所有端的數(shù)據(jù)采集規(guī)范都基于 OTel,數(shù)據(jù)存儲(chǔ)、處理、分析是基于 SLS 提供的 LogHub 能力進(jìn)行構(gòu)建。
端側(cè)數(shù)據(jù)采集的難點(diǎn)
只統(tǒng)一數(shù)據(jù)協(xié)議還不夠,還要解決端側(cè)在數(shù)據(jù)采集方面存在的一些問(wèn)題??偟膩?lái)說(shuō),端側(cè)采集當(dāng)前面臨 3 個(gè)主要的難點(diǎn):
數(shù)據(jù)串聯(lián)難
性能保障難
不丟數(shù)據(jù)難
端側(cè)研發(fā)過(guò)程中涉及到的框架、模塊往往比較多,業(yè)務(wù)也有一定的復(fù)雜性,存在線程、協(xié)程多種異步調(diào)用 API,在數(shù)據(jù)采集過(guò)程中,如何解決數(shù)據(jù)之間的自動(dòng)串聯(lián)問(wèn)題?移動(dòng)端設(shè)備碎片化嚴(yán)重,系統(tǒng)版本分布比較散,機(jī)型眾多,如何保障多端一致的采集性能?App 使用場(chǎng)景的不確定性也比較大,如何確保采集到的數(shù)據(jù)不會(huì)丟失?
端側(cè)數(shù)據(jù)串聯(lián)的難點(diǎn)
我們先來(lái)分析一下端側(cè)數(shù)據(jù)自動(dòng)串聯(lián)所面臨的主要問(wèn)題。
在端側(cè)數(shù)據(jù)采集過(guò)程中,不僅會(huì)采集業(yè)務(wù)鏈路數(shù)據(jù),還會(huì)采集各種性能&穩(wěn)定性監(jiān)控?cái)?shù)據(jù),可觀測(cè)數(shù)據(jù)源比較多;
如果用到其他的研發(fā)框架,如 OkHttp、Fresco 等,可能還會(huì)采集三方框架的關(guān)鍵數(shù)據(jù)用于網(wǎng)絡(luò)請(qǐng)求,圖片加載等問(wèn)題的分析和定位。對(duì)于業(yè)務(wù)研發(fā)同學(xué)來(lái)說(shuō),我們往往不會(huì)過(guò)多的關(guān)注這類(lèi)三方框架技術(shù)能力,涉及到這類(lèi)框架問(wèn)題的排查時(shí),過(guò)程往往比較困難;
除此之外,端側(cè)幾乎完全異步調(diào)用,而且異步調(diào)用 API 比較多,如線程、協(xié)程等,鏈路打通也存在一定的挑戰(zhàn)。
這里會(huì)有幾個(gè)共性問(wèn)題:
三方框架的數(shù)據(jù)如何采集?如何串聯(lián)?
不同可觀測(cè)數(shù)據(jù)源之間如何串聯(lián)?
分布在不同線程、協(xié)程之間的數(shù)據(jù)如何自動(dòng)串聯(lián)?
端側(cè)數(shù)據(jù)自動(dòng)串聯(lián)方案
我們先看下端側(cè)數(shù)據(jù)自動(dòng)串聯(lián)的方案。
在 OTel 協(xié)議標(biāo)準(zhǔn)中,是通過(guò) trace 協(xié)議來(lái)約束不同數(shù)據(jù)之間的串聯(lián)關(guān)系。OTel 定義了 trace 數(shù)據(jù)鏈路中每條數(shù)據(jù)必須要包含的必要字段,我們需要確保同一條鏈路中數(shù)據(jù)的一致性。比如,同一條 trace 鏈路中,trace_id 需要相同;其次,如果數(shù)據(jù)之間有父子關(guān)系,子數(shù)據(jù)的 parent_id 也需要與父數(shù)據(jù)的 span_id 相同。
我們知道,不管是 Android 平臺(tái),還是 iOS 平臺(tái),線程都是操作系統(tǒng)能夠調(diào)度的最小單元。也就是說(shuō),我們所有的代碼,最終都會(huì)在線程中被執(zhí)行。在代碼被執(zhí)行過(guò)程中,如果我們能把上下文信息和當(dāng)前線程進(jìn)行關(guān)聯(lián),在代碼執(zhí)行時(shí),就能自動(dòng)獲取當(dāng)前上下文信息,這樣就可以解決同一個(gè)線程內(nèi)的 trace 數(shù)據(jù)自動(dòng)關(guān)聯(lián)問(wèn)題。
在 Android 中,可以基于線程變量 ThreadLocal 來(lái)存儲(chǔ)當(dāng)前線程棧的上下文信息,這樣可以確保在同一線程中采集到的業(yè)務(wù)數(shù)據(jù)進(jìn)行自動(dòng)關(guān)聯(lián)。如果是在協(xié)程中使用,基于線程變量的方案就會(huì)存在問(wèn)題。因?yàn)樵趨f(xié)程中,協(xié)程真實(shí)運(yùn)行的線程是不確定的,可能會(huì)在協(xié)程執(zhí)行的生命周期內(nèi)進(jìn)行線程切換,我們需要利用協(xié)程調(diào)度器和協(xié)程 Context 來(lái)保持當(dāng)前上下文的正確性。在協(xié)程恢復(fù)時(shí),讓關(guān)聯(lián)的上下文信息在當(dāng)前線程生效,在協(xié)程掛起時(shí),再讓上下文信息在當(dāng)前線程失效。
在 iOS 中,主要基于 activity tracing 機(jī)制來(lái)保持上下文信息的有效性。通過(guò) activity tracing 機(jī)制,在一個(gè)業(yè)務(wù)鏈路開(kāi)始時(shí),會(huì)自動(dòng)創(chuàng)建一個(gè) activity,我們把上下文信息與 activity 進(jìn)行關(guān)聯(lián)。在當(dāng)前 activity 作用域范圍內(nèi),所有產(chǎn)生的數(shù)據(jù)都會(huì)與當(dāng)前上下文自動(dòng)關(guān)聯(lián)。
基于這兩種方案,在產(chǎn)生 Trace 數(shù)據(jù)時(shí),SDK 會(huì)按照 OTel 協(xié)議的標(biāo)準(zhǔn),自動(dòng)把上下文信息關(guān)聯(lián)到當(dāng)前數(shù)據(jù)中。最終產(chǎn)生的數(shù)據(jù),會(huì)以一棵樹(shù)的形式進(jìn)行邏輯關(guān)聯(lián),樹(shù)的根節(jié)點(diǎn)就是 Trace 鏈路的起點(diǎn)。這種方式,不僅支持協(xié)程/線程內(nèi)的數(shù)據(jù)自動(dòng)關(guān)聯(lián),還支持多層級(jí)嵌套。
三方框架的數(shù)據(jù)采集和串聯(lián)
針對(duì)三方框架的數(shù)據(jù)采集,我們先看看業(yè)內(nèi)通行的做法,目前主要有兩類(lèi):
如果三方庫(kù)支持?jǐn)r截器或代理的配置,一般會(huì)通過(guò)在對(duì)應(yīng)攔截器增加埋點(diǎn)代碼的方式來(lái)實(shí)現(xiàn);
如果三方庫(kù)對(duì)外暴露的接口比較少,一般會(huì)通過(guò) Hook 或其他方式增加埋點(diǎn)代碼,或者不支持對(duì)應(yīng)框架的埋點(diǎn)。
這種做法會(huì)存在兩個(gè)主要的問(wèn)題:
埋點(diǎn)不完全,拿 OkHttp 來(lái)舉例說(shuō)明,三方 SDK 內(nèi)部也可能存在對(duì) OkHttp 的依賴(lài),通過(guò)攔截器的方式,可能只支持當(dāng)前業(yè)務(wù)代碼的埋點(diǎn)采集,三方 SDK 的網(wǎng)絡(luò)請(qǐng)求信息無(wú)法被采集到,會(huì)導(dǎo)致埋點(diǎn)信息不完全;
可能需要侵入業(yè)務(wù)代碼,為了實(shí)現(xiàn)對(duì)應(yīng)框架的埋點(diǎn),需要有一個(gè)切入時(shí)機(jī),這個(gè)切入時(shí)機(jī)往往需要在對(duì)應(yīng)框架初始化時(shí)增加代碼配置項(xiàng)來(lái)實(shí)現(xiàn)。
如何解這兩個(gè)問(wèn)題?
我們使用的方案是實(shí)現(xiàn)一個(gè) Gradle Plugin,在 Plugin 中對(duì)字節(jié)碼進(jìn)行插樁處理。我們知道,Android App 在打包的過(guò)程中,有個(gè)流程會(huì)把 .class 文件轉(zhuǎn)為 .dex 文件,在這個(gè)過(guò)程中,可以通過(guò) transform api 對(duì) class 文件進(jìn)行處理。我們是借助 ASM 的方式來(lái)實(shí)現(xiàn) class 文件的插樁處理。在對(duì)字節(jié)碼處理的過(guò)程中,需要先找到合適的插樁點(diǎn),然后注入合適的指令。
這里拿 OkHttp 的字節(jié)插樁進(jìn)行舉例:插樁的目標(biāo)是在 OkHttpClient 調(diào)用 newCall 方法時(shí),把當(dāng)前線程的上下文信息關(guān)聯(lián)到 OkHttp 的 Request 中。在 Transform 過(guò)程中,我們先根據(jù) OkHttpClient 的類(lèi)名過(guò)濾出目標(biāo) class 文件,然后再根據(jù) newCall 這個(gè)方法名過(guò)濾要插樁的方法。接下來(lái),需要在 newCall 方法開(kāi)始的地方把上下文信息插入到 request 的 tags 對(duì)象中。經(jīng)過(guò)我們的分析,需要在 newCall 方法調(diào)用開(kāi)始的時(shí)候,插入目標(biāo)代碼。為了方便實(shí)現(xiàn)和調(diào)試,我們?cè)跀U(kuò)展庫(kù)中實(shí)現(xiàn)了一個(gè) OkHttp 的輔助工具,在目標(biāo)位置插入調(diào)用這個(gè)工具的字節(jié)碼,傳入 request 對(duì)象就可以了。
插入后的字節(jié)碼會(huì)和擴(kuò)展庫(kù)進(jìn)行關(guān)聯(lián)。這樣就能解決三方框架數(shù)據(jù)采集和上下文自動(dòng)關(guān)聯(lián)的問(wèn)題。
相對(duì)于傳統(tǒng)做法,使用字節(jié)碼插樁的方案,業(yè)務(wù)代碼侵入性會(huì)更低,埋點(diǎn)對(duì)業(yè)務(wù)代碼和三方框架都能生效,同時(shí)結(jié)合擴(kuò)展庫(kù)也能完成上下文的自動(dòng)關(guān)聯(lián)。
如何確保性能
在可觀測(cè)數(shù)據(jù)采集過(guò)程中,會(huì)有大量的數(shù)據(jù)產(chǎn)生,對(duì)內(nèi)存、CPU 占用、I/O 負(fù)載都有一定的性能要求。
我們基于 C 對(duì)核心部分進(jìn)行實(shí)現(xiàn),確保多平臺(tái)的性能一致性,并從三個(gè)方面對(duì)性能做了優(yōu)化:
首先,是對(duì)協(xié)議化處理過(guò)程進(jìn)行優(yōu)化。數(shù)據(jù)協(xié)議方面選擇使用 Protocal Buffer 協(xié)議,Protocal Buffer 相對(duì) JSON 來(lái)說(shuō),不僅速度更快,而且更省內(nèi)存空間。在協(xié)議的序列化上,我們采用了手動(dòng)封裝協(xié)議的實(shí)現(xiàn),在序列化的過(guò)程中,避免了很多臨時(shí)內(nèi)存空間的開(kāi)辟、復(fù)制以及無(wú)關(guān)函數(shù)的調(diào)用。
其次,在內(nèi)存管理方面,我們直接對(duì) SDK 的最大使用內(nèi)存做了可配置的大小限制。內(nèi)存的使用,可以根據(jù)業(yè)務(wù)情況按需配置,避免 SDK 內(nèi)存占用過(guò)大對(duì) App 的穩(wěn)定性造成影響;其次,還引入了動(dòng)態(tài)內(nèi)存管理機(jī)制,內(nèi)存空間的使用按需增加,不會(huì)一直占用 App 的內(nèi)存空間,避免內(nèi)存空間的浪費(fèi)。同時(shí)還提升了字符串的處理性能。在字符的處理上,引入了動(dòng)態(tài)字符串機(jī)制,它可以記錄字符串自身的長(zhǎng)度,獲取字符長(zhǎng)度時(shí),操作復(fù)雜度低,而且可以避免緩沖區(qū)溢出,同時(shí)也可以減少修改字符串時(shí)帶來(lái)的內(nèi)存重分配次數(shù)。
最后,在文件緩存管理方面,我們也限制了文件大小的上限,避免對(duì)端設(shè)備存儲(chǔ)空間的浪費(fèi)。在緩存文件的落盤(pán)處理上,我們引入了 Ring File 機(jī)制,把緩存數(shù)據(jù)存儲(chǔ)在多個(gè)文件上面,以日志文件組的形式對(duì)多個(gè)文件進(jìn)行組裝。整個(gè)日志文件組以環(huán)形數(shù)組的形式,從頭開(kāi)始寫(xiě),寫(xiě)到末尾再回到頭重新循環(huán)寫(xiě)。
通過(guò)這種方式寫(xiě)數(shù)據(jù),可以減少寫(xiě)文件時(shí)的隨機(jī) Seek,而且 Ring File 的機(jī)制,可以確保單個(gè)日志文件不會(huì)過(guò)大,從而盡可能的降低系統(tǒng) I/O 的負(fù)載。除了 Ring File 的機(jī)制外,還把斷點(diǎn)保存、緩存清理的邏輯放到了一起聚合執(zhí)行,減少隨機(jī) Seek。checkpoint 的文件大小也做了限制,在超出指定大小后會(huì)對(duì) checkpoint 文件進(jìn)行清理,避免 checkpoint 文件過(guò)大影響文件讀寫(xiě)效率。
經(jīng)過(guò)上面的這些優(yōu)化措施之后,最終 SDK 采集數(shù)據(jù)的吞吐量提升了 2 倍,內(nèi)存和 CPU 占用都有明顯的降低。每秒鐘最高可支持 400+條數(shù)據(jù)的采集。
如何確保日志不丟失?
性能滿足要求還不夠,還需要確保采集到的數(shù)據(jù)不能丟失。在 App 的使用過(guò)程中,app 經(jīng)??赡軙?huì)出現(xiàn)異常崩潰,手機(jī)設(shè)備異常重啟,以及網(wǎng)絡(luò)質(zhì)量差,網(wǎng)絡(luò)延時(shí)、抖動(dòng)大的情況。在這類(lèi)異常場(chǎng)景下,如何確保采集到數(shù)據(jù)不會(huì)丟失?
在采集數(shù)據(jù)時(shí),我們使用了預(yù)寫(xiě)日志(WAL)機(jī)制,并結(jié)合自建網(wǎng)絡(luò)加速通道來(lái)優(yōu)化這個(gè)問(wèn)題。
引入預(yù)寫(xiě)日志機(jī)制的目的是確保寫(xiě)入到 SDK 的數(shù)據(jù),在發(fā)送到服務(wù)器之前,不會(huì)因?yàn)楫惓T蚨鴣G失。這個(gè)過(guò)程的核心是,在數(shù)據(jù)成功發(fā)送到服務(wù)器之前,先把數(shù)據(jù)緩存在移動(dòng)設(shè)備的磁盤(pán)上,數(shù)據(jù)發(fā)送成功之后,再移除磁盤(pán)上的緩存數(shù)據(jù)。如果因?yàn)?App 異常原因,或者設(shè)備重啟導(dǎo)致數(shù)據(jù)發(fā)送失敗,因?yàn)榫彺娴臄?shù)據(jù)還在,SDK 會(huì)根據(jù)記錄的斷點(diǎn)信息對(duì)數(shù)據(jù)發(fā)送進(jìn)度進(jìn)行恢復(fù)。同時(shí)預(yù)寫(xiě)日志機(jī)制可以確保數(shù)據(jù)的寫(xiě)入和發(fā)送并發(fā)執(zhí)行,不會(huì)互相阻塞;
在數(shù)據(jù)發(fā)送之前,還會(huì)對(duì)多條數(shù)據(jù)做聚合處理,并通過(guò) lz4 算法進(jìn)行壓縮處理,這種做法可以降低數(shù)據(jù)發(fā)送時(shí)的請(qǐng)求次數(shù)和網(wǎng)絡(luò)傳輸流量的消耗。如果數(shù)據(jù)發(fā)送失敗,還會(huì)有重試策略,確保數(shù)據(jù)至少能成功發(fā)送一次;
在數(shù)據(jù)發(fā)送時(shí),SDK 支持就近接入加速邊緣節(jié)點(diǎn),并通過(guò)邊緣節(jié)點(diǎn)與 SLS 之間的內(nèi)部網(wǎng)絡(luò)加速通道傳輸數(shù)據(jù)。
經(jīng)過(guò)這三種主要的方式優(yōu)化之后,數(shù)據(jù)包的平均大小降低了 2.1 倍,整體的 QPS 平均提升 13 倍,數(shù)據(jù)整體的發(fā)送成功率達(dá)到了 99.3%,網(wǎng)絡(luò)延時(shí)平均下降了 50%。
多系統(tǒng)數(shù)據(jù)關(guān)聯(lián)處理
解決了端側(cè)數(shù)據(jù)的串聯(lián)和采集性能問(wèn)題之后,還需要處理多系統(tǒng)之間的數(shù)據(jù)存儲(chǔ)和關(guān)聯(lián)分析問(wèn)題。
數(shù)據(jù)存儲(chǔ)方面,我們直接基于 SLS LogHub 能力,把相關(guān)的數(shù)據(jù)統(tǒng)一存儲(chǔ),基于 SLS,日均可以承載 PB 級(jí)別的流量,這個(gè)吞吐量可以支持移動(dòng)端可觀測(cè)數(shù)據(jù)的全量采集。
解決了數(shù)據(jù)的統(tǒng)一存儲(chǔ)問(wèn)題之后,還需要處理兩個(gè)主要的問(wèn)題。
第一個(gè)問(wèn)題,不同系統(tǒng)可觀測(cè)數(shù)據(jù)之間的上下文關(guān)聯(lián)如何處理?
根據(jù) OTel 協(xié)議的約束,我們可以基于 parent_id 和 span_id 來(lái)處理根節(jié)點(diǎn)、父節(jié)點(diǎn)、子節(jié)點(diǎn)之間的映射關(guān)系。首先,在查詢(xún) Trace 數(shù)據(jù)鏈路時(shí),會(huì)先從 SLS 拉取一定時(shí)間段內(nèi)的所有 Trace 數(shù)據(jù)。然后按照 OTel 協(xié)議的約束,對(duì)每條數(shù)據(jù)進(jìn)行節(jié)點(diǎn)類(lèi)型的判定。
由于多系統(tǒng)的數(shù)據(jù)可能存在延時(shí),在查詢(xún) Trace 數(shù)據(jù)鏈路時(shí),有些數(shù)據(jù)可能還沒(méi)有到達(dá)。我們還需要對(duì)暫時(shí)不存在的父節(jié)點(diǎn)進(jìn)行虛擬化處理,確保 Trace 鏈路的準(zhǔn)確性。接下來(lái),還需要對(duì)節(jié)點(diǎn)進(jìn)行規(guī)整處理,把屬于同一個(gè) parent_id 的節(jié)點(diǎn)進(jìn)行聚合,然后再按照每個(gè)節(jié)點(diǎn)的開(kāi)始時(shí)間進(jìn)行排序,最終就可以得到一條 trace 鏈路信息,基于這個(gè)鏈路信息,我們可以還原出系統(tǒng)的調(diào)用鏈路。
第二個(gè)問(wèn)題, 在進(jìn)行 Trace 分析時(shí),我們往往還需要從系統(tǒng)視角出發(fā),對(duì)不同維度的數(shù)據(jù)進(jìn)一步分析。比如,如果想從設(shè)備 ID、App 版本、服務(wù)調(diào)用等不同維度,對(duì) Trace 數(shù)據(jù)進(jìn)一步分析,該怎么做?我們來(lái)看一下怎么解決這個(gè)問(wèn)題。
多系統(tǒng)數(shù)據(jù)拓?fù)渖?/p>
當(dāng)我們從系統(tǒng)整體視角對(duì)問(wèn)題進(jìn)行分析時(shí),所需要的 Trace 數(shù)據(jù)規(guī)模往往會(huì)比較大,每分鐘可能有數(shù)千萬(wàn)條數(shù)據(jù),而且對(duì)數(shù)據(jù)的時(shí)效性要求也比較高。傳統(tǒng)的流處理方式在這種場(chǎng)景下很容易遇到性能瓶頸問(wèn)題。我們采用的方案是,把流處理問(wèn)題轉(zhuǎn)換為批處理問(wèn)題,把傳統(tǒng)的鏈路處理視角轉(zhuǎn)換為系統(tǒng)處理視角。經(jīng)過(guò)視角轉(zhuǎn)化之后,從系統(tǒng)視角來(lái)看,解決這個(gè)問(wèn)題最主要的核心,就是如何確定兩個(gè)節(jié)點(diǎn)之間的關(guān)系。
我們看一下具體的處理過(guò)程。在批處理上,我們使用了 MapReduce 框架。首先,在數(shù)據(jù)源處理階段,我們基于 SLS 的定時(shí)分析(ScheduledSQL)能力,對(duì)數(shù)據(jù)進(jìn)行聚合處理,按照分鐘級(jí)從 Trace 數(shù)據(jù)源中撈取數(shù)據(jù)。在 Map 階段,先按照 traceID 進(jìn)行分組,對(duì)分組之后的數(shù)據(jù)再按照 spanID、parentID 維度對(duì)數(shù)據(jù)進(jìn)行聚合。
然后計(jì)算出相關(guān)的統(tǒng)計(jì)數(shù)據(jù),如成功率、失敗率、延時(shí)指標(biāo)等基礎(chǔ)統(tǒng)計(jì)數(shù)據(jù)。在實(shí)際的業(yè)務(wù)使用中,往往還會(huì)采集一些和具體業(yè)務(wù)屬性相關(guān)的數(shù)據(jù),這部分?jǐn)?shù)據(jù)往往會(huì)根據(jù)業(yè)務(wù)的不同,有比較大的差異。針對(duì)這部分類(lèi)型的數(shù)據(jù),在聚合處理的過(guò)程中,支持按照其他維度對(duì)結(jié)果進(jìn)行分組。此時(shí)會(huì)得到兩種中間產(chǎn)物:
包含兩個(gè)節(jié)點(diǎn)關(guān)系的聚合數(shù)據(jù),我們把這種類(lèi)型的數(shù)據(jù),叫做邊信息
以及未匹配到的原始數(shù)據(jù)
這兩種中間產(chǎn)物,在 Combine 階段還會(huì)再進(jìn)行聚合處理,最終會(huì)得到包含基礎(chǔ)統(tǒng)計(jì)指標(biāo),以及其他維度的結(jié)果數(shù)據(jù)。
最終產(chǎn)物會(huì)包含幾個(gè)主要的信息:
邊信息,可以體現(xiàn)調(diào)用關(guān)系。
依賴(lài)信息,可以體現(xiàn)服務(wù)依賴(lài)關(guān)系。
還有指標(biāo)信息,以及其他資源信息等。其中,業(yè)務(wù)屬性相關(guān)的數(shù)據(jù)會(huì)體現(xiàn)在資源信息中。
基于這些產(chǎn)物,我們可以通過(guò)對(duì)資源、服務(wù)等信息的多個(gè)維度篩選,來(lái)統(tǒng)計(jì)出對(duì)應(yīng)維度的問(wèn)題分布和影響鏈路。
自動(dòng)化問(wèn)題根因定位探索
接下來(lái)向大家介紹下,我們?cè)谧詣?dòng)化問(wèn)題根因定位方向的一些探索。
我們知道,隨著 App 版本的迭代,每次 App 的發(fā)版可能會(huì)涉及到多個(gè)業(yè)務(wù)的代碼變更。這些變更,有的經(jīng)過(guò)充分測(cè)試,也有的未經(jīng)過(guò)充分測(cè)試,或者常規(guī)測(cè)試方法沒(méi)有覆蓋到,對(duì)線上業(yè)務(wù)可能會(huì)產(chǎn)生一定的潛在影響,導(dǎo)致部分業(yè)務(wù)不可用。App 規(guī)模越大,業(yè)務(wù)模式越多,對(duì)應(yīng)的業(yè)務(wù)數(shù)據(jù)量,請(qǐng)求鏈路,不確定性就越大。出了問(wèn)題之后,往往需要多人跨域參與排查,人肉運(yùn)維成本比較高。
如何在端側(cè)問(wèn)題排查定位方向,通過(guò)技術(shù)手段進(jìn)行研發(fā)效能的提速? 我們基于機(jī)器學(xué)習(xí)技術(shù)做了一些探索。
我們目前的方法是,先對(duì) Trace 源數(shù)據(jù)進(jìn)行特征處理;然后再對(duì)特征進(jìn)行聚類(lèi)分析,去找到異常 Trace;最后再基于圖算法等,對(duì)異常 Trace 進(jìn)行分析,找到異常的起始點(diǎn)。
首先,實(shí)時(shí)特征處理階段會(huì)讀取 Trace 源數(shù)據(jù),對(duì)每個(gè) Trace 鏈路按照由底向上找 5 個(gè)節(jié)點(diǎn)的方式生成一個(gè)特征,并對(duì)特征進(jìn)行編碼。然后對(duì)編碼之后的特征通過(guò) HDBSCAN 算法進(jìn)行層次聚類(lèi)分析,此時(shí)相似的異常會(huì)分到同一個(gè)組里面,接下來(lái)再?gòu)拿拷M異常 Trace 中找出一條典型的異常 Trace。最后,通過(guò)圖算法找到這條異常 Trace 的起點(diǎn),從而確定當(dāng)前異常 Trace 可能存在的問(wèn)題根因。通過(guò)這種方式,只要是遵循 OTel 標(biāo)準(zhǔn)協(xié)議的數(shù)據(jù)源都能夠進(jìn)行處理。
案例:多端鏈路追蹤
經(jīng)過(guò)對(duì)數(shù)據(jù)處理之后,我們來(lái)看下最終的效果。
這里有一個(gè)模擬 Android、iOS、服務(wù)端,端到端鏈路追蹤的場(chǎng)景。
我們使用 iOS App 來(lái)作為指令的發(fā)送端,Android App 來(lái)作為指令的響應(yīng)端,用來(lái)模擬遠(yuǎn)程打開(kāi)汽車(chē)空調(diào)的操作。我們從圖上可以看到,iOS 端“打開(kāi)車(chē)機(jī)空調(diào)”這個(gè)操作觸發(fā)后,依次經(jīng)過(guò)了“用戶(hù)權(quán)限校驗(yàn)”、“發(fā)送指令”、“調(diào)用網(wǎng)絡(luò)請(qǐng)求”等環(huán)節(jié)。Android 端收到指令后,依次執(zhí)行“遠(yuǎn)程啟動(dòng)空調(diào)”、“狀態(tài)檢查”等環(huán)節(jié)。
從這個(gè)調(diào)用圖可以看得到,Android、iOS、服務(wù)端,多端鏈路被串聯(lián)到了一起。我們可以從 Android、iOS、服務(wù)端的任何一個(gè)視角,對(duì)調(diào)用鏈路進(jìn)行分析。每個(gè)操作的耗時(shí),對(duì)應(yīng)服務(wù)的請(qǐng)求數(shù),錯(cuò)誤率,以及服務(wù)依賴(lài)都能體現(xiàn)出來(lái)。
整體架構(gòu)
接下來(lái),我們來(lái)看下整套解決方案的架構(gòu):
最底層是數(shù)據(jù)源,遵循 OTel 協(xié)議,各個(gè)端對(duì)應(yīng)的 SDK 按照協(xié)議規(guī)范統(tǒng)一實(shí)現(xiàn);
數(shù)據(jù)存儲(chǔ)層,是直接依托于 SLS LogHub,所有系統(tǒng)采集到的數(shù)據(jù)統(tǒng)一存儲(chǔ);
再往上是數(shù)據(jù)處理層,對(duì)關(guān)鍵指標(biāo)、Trace 鏈路、依賴(lài)關(guān)系、拓?fù)浣Y(jié)構(gòu)、還有特征等進(jìn)行了預(yù)處理。
最后是上層應(yīng)用,提供鏈路分析、拓?fù)洳樵?xún)、指標(biāo)查詢(xún)、原始日志查詢(xún),以及根因定位等能力
后續(xù)規(guī)劃
最后總結(jié)下我們后續(xù)的規(guī)劃:
在采集層,會(huì)繼續(xù)完善插件、注解等方式的支持,降低業(yè)務(wù)代碼的侵入性,提升接入效率
在數(shù)據(jù)側(cè),會(huì)豐富可觀測(cè)數(shù)據(jù)源,后續(xù)會(huì)支持網(wǎng)絡(luò)質(zhì)量、性能等相關(guān)數(shù)據(jù)的采集
在應(yīng)用側(cè),會(huì)提供用戶(hù)訪問(wèn)監(jiān)測(cè)、性能分析等能力
審核編輯:劉清
-
API
+關(guān)注
關(guān)注
2文章
1508瀏覽量
62230 -
Trace
+關(guān)注
關(guān)注
0文章
19瀏覽量
10572 -
SDK
+關(guān)注
關(guān)注
3文章
1044瀏覽量
46095 -
SLS
+關(guān)注
關(guān)注
0文章
15瀏覽量
8927 -
調(diào)度器
+關(guān)注
關(guān)注
0文章
98瀏覽量
5264
原文標(biāo)題:SLS:基于 OTel 的移動(dòng)端全鏈路 Trace 建設(shè)思考和實(shí)踐
文章出處:【微信號(hào):OSC開(kāi)源社區(qū),微信公眾號(hào):OSC開(kāi)源社區(qū)】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
評(píng)論