本文來自騰訊手Q基礎(chǔ)架構(gòu)團(tuán)隊(duì)楊蕭玉、邱少雄、張自蹊、王褚重天、姚偉斌的分享,原題“QQ 客戶端性能穩(wěn)定性防劣化系統(tǒng) Hodor 技術(shù)方案”,下文進(jìn)行了排版和內(nèi)容優(yōu)化。
1、引言
接上篇《首次公開,最新手機(jī)QQ客戶端架構(gòu)的技術(shù)演進(jìn)實(shí)踐》。
防劣化是比較經(jīng)典的技術(shù)話題,手 Q 的防劣化系統(tǒng)從 2021 年 10 月開始投入研發(fā),從 0 到 1 迭代了將近三年的時(shí)間,已經(jīng)達(dá)到了業(yè)界先進(jìn)水平。為了守護(hù)好手 Q 性能穩(wěn)定性的門禁,我們將其命名為 Hodor 系統(tǒng),即 Hold the door!
從驗(yàn)證可行性跑通最小閉環(huán),到搭建群控機(jī)架一次次為集群擴(kuò)容,實(shí)屬不易。其中涉及到大量的方案討論甚至推翻,很多思路和實(shí)現(xiàn)細(xì)節(jié)是業(yè)界找不到公開方案的,只能自己摸索。
本文以iOS端為例,詳細(xì)分享了手 Q 客戶端性能防劣化系統(tǒng)從0到1的構(gòu)建之路,相信對(duì)業(yè)界和IM開發(fā)者們都有較高的借鑒意義。
2、為什么要做防劣化
主要有以下原因:
- 1)代碼體量較大:業(yè)務(wù)涵蓋 IM/空間/短視頻/超級(jí) QQ 秀等;
- 2)迭代需求緊:雙周迭代,研發(fā)人員多;每版本幾十條需求分支,主干每周構(gòu)建出包數(shù)百次;
- 3)問題較多:需求合流新增性能問題多,基礎(chǔ)側(cè)人力寡不敵眾,問題越堆越多,事后回溯效率低。
當(dāng)業(yè)務(wù)的體量足夠大,問題足夠復(fù)雜的時(shí)候,解決問題的思路也需要轉(zhuǎn)變。
3、如何破局
盤點(diǎn)了下手 Q 研發(fā)流程的困局,現(xiàn)有的手段更著重于線上監(jiān)控問題并在下個(gè)版本修復(fù)(甚至是下下個(gè)版本),如果能在開發(fā)階段發(fā)布前甚至合入 master 之前就把問題扼殺在搖籃之中,就可以達(dá)到防劣化的目標(biāo)。
下圖根據(jù)手 Q 真實(shí)慘痛案例改編,當(dāng)年為了查一個(gè)嚴(yán)重的啟動(dòng)耗時(shí)劣化問題,二分法拉代碼手動(dòng)跑 Instruments 的痛,懂的都懂。
(此聊天記錄為虛構(gòu),如有雷同純屬巧合)
大家開發(fā)需求都愛趕 deadline,所以合流高峰期光靠堆人力代碼 CR 和手動(dòng)測(cè)試性能是不現(xiàn)實(shí)的,性能問題漏出事后優(yōu)化也是不夠的。因?yàn)闃I(yè)務(wù)的復(fù)雜性,總是優(yōu)化趕不上劣化快。
手 Q 在優(yōu)化性能穩(wěn)定性的同時(shí),也提前布局防劣化系統(tǒng),將其作為質(zhì)量三位一體中的重要一環(huán)(如下圖所示)。
4、防劣化系統(tǒng)的目標(biāo)
提前發(fā)現(xiàn)部分主路徑問題,通過門禁防止性能劣化:
- 1)主干合流門禁:對(duì)于較穩(wěn)定的性能指標(biāo),合流前自動(dòng)檢查;
- 2)日常自動(dòng)提單:針對(duì)偶現(xiàn)的性能問題,開發(fā)階段提前發(fā)現(xiàn);
- 3)性能數(shù)據(jù)看板:常態(tài)化詳細(xì)數(shù)據(jù)看板,上帝視角觀測(cè)性能;
- 4)告警機(jī)器人:自定義各性能維度告警規(guī)則,第一時(shí)間發(fā)現(xiàn)問題。
5、防劣化系統(tǒng)的實(shí)現(xiàn)
因?yàn)檎紫到y(tǒng)的實(shí)現(xiàn)比較復(fù)雜,考慮到整體篇幅限制,數(shù)據(jù)采集部分僅概述 iOS 平臺(tái)的方案,各平臺(tái)數(shù)據(jù)的上報(bào)協(xié)議及服務(wù)端的處理邏輯是共享的。
6、方案設(shè)計(jì)
6.1概述
要做好門禁,就需要把性能數(shù)據(jù)精確到每一次 commit,并做好科學(xué)的對(duì)比。
現(xiàn)實(shí)情況是很復(fù)雜的,可能有各種各樣的突發(fā)情況:
基于以上訴求,我們開發(fā)了 feature 分支對(duì)比 master 的算法策略。建立全維度性能指標(biāo)和科學(xué)歸因方案。Hodor 實(shí)現(xiàn)了性能報(bào)告、數(shù)據(jù)分析、智能調(diào)度、提單告警、設(shè)備管理、用例管理等一系列能力。
大概的運(yùn)行機(jī)制如下:
此方案的優(yōu)點(diǎn):
- 1)性能測(cè)試和性能報(bào)告創(chuàng)建審批左移到開發(fā)階段;
- 2)覆蓋場(chǎng)景可拓展:測(cè)試用例云端獨(dú)立管理派發(fā);
- 3)性能維度可拓展:支持 Instruments 所有模板;
- 4)靜態(tài)檢查可拓展:構(gòu)建數(shù)據(jù)服務(wù)端存儲(chǔ)與比對(duì)。
6.2防劣化 ≠ 自動(dòng)化測(cè)試
現(xiàn)有測(cè)試平臺(tái)和工具的不足:
- 1)測(cè)試平臺(tái)站在發(fā)現(xiàn)問題角度,缺乏解決問題思維;
- 2)APM SDK 運(yùn)行時(shí)采集能力受限。
詳細(xì)來說,就是:
- 1)傳統(tǒng)的自動(dòng)化性能測(cè)試平臺(tái)和工具的報(bào)告只有 metric 統(tǒng)計(jì)數(shù)據(jù)和 App 截圖/日志,信息太少不足以定位問題;
- 2)APM 平臺(tái)的線上監(jiān)控很強(qiáng)大,但無法動(dòng)態(tài)追蹤采集 time profiler 等詳細(xì)數(shù)據(jù)。
所以解決問題依然需要開發(fā)同學(xué)獲取更詳細(xì)信息或能夠 debug 復(fù)現(xiàn)問題,而性能問題往往是偶現(xiàn)的。
自動(dòng)防劣化解決方案:打通發(fā)現(xiàn)和解決問題全鏈路。
主要是:
- 1)通過 Instruments 動(dòng)態(tài)追蹤技術(shù)采集 diagnostic 診斷數(shù)據(jù),無侵入性;
- 2)xctrace 自動(dòng)解析 trace 文件,翻譯堆棧精準(zhǔn)歸因,還原『案發(fā)現(xiàn)場(chǎng)』;
- 3)每次提交構(gòu)建均執(zhí)行防劣化檢測(cè),精準(zhǔn)定位問題的提交引入者;
- 4)數(shù)據(jù)可視化看板+自動(dòng)提單派發(fā)+大模型 AI 分析問題 = 測(cè)開降本增效。
總之,手 Q 的解決方案從一開始就打破了傳統(tǒng)思維,最終的方案也是真香!
6.3復(fù)雜業(yè)務(wù)下如何降低性能波動(dòng)
俗話說細(xì)節(jié)決定成敗,很多事情想跑通 demo 很容易,但是想達(dá)到高可用性,需要打磨很多很多細(xì)節(jié)才能應(yīng)用到生產(chǎn)環(huán)境。比如為了控制變量降低場(chǎng)外因素波動(dòng),大到集群調(diào)度策略,小到機(jī)器零件型號(hào),都錙銖必較。
7、數(shù)據(jù)采集實(shí)現(xiàn)
在運(yùn)行時(shí)性能數(shù)據(jù)采集方面,我們擁有一套自研方案;在靜態(tài)掃描方面,也從編譯鏈接過程采集了相關(guān)數(shù)據(jù)。
7.1 動(dòng)態(tài)性能數(shù)據(jù)采集
性能采集方案選型之初,我們對(duì)于性能采集主要有以下幾個(gè)訴求:
- 1)需要有詳細(xì)的堆棧信息;
- 2)性能維度足夠多;
- 3)最好非侵入式的;
- 4)容易與 CI 流程相結(jié)合。
為了滿足上述訴求,最終我們選則了當(dāng)時(shí)蘋果剛推出的 xctrace 方案。
7.1.1)應(yīng)用外數(shù)據(jù)采集:
[1] xctrace:
Instruments 是 iOS & macOS 平臺(tái)進(jìn)行性能分析必不可少的工具,它能采集到絕大多數(shù)的性能數(shù)據(jù),同時(shí)擁有精美的 GUI 方便排查和分析。但是 Instruments一直都只有 GUI(古早年代曾經(jīng)有過導(dǎo)出性能數(shù)據(jù)的功能,后面也去掉了),沒有 CLI,這也使自動(dòng)化使用 Instruments 進(jìn)行性能采集和分析成為奢望。直到 Xcode 12,蘋果終于推出了 Instruments 的 CLI 版本:xctrace。
xctrace 提供了一系列命令,可以錄制、導(dǎo)出性能數(shù)據(jù),Instruments 支持哪些性能維度,xctrace 就支持哪些性能維度。我們根據(jù)不同的性能采集需求,生成不同的性能模板,使用 xctrace 錄制對(duì)應(yīng)的性能數(shù)據(jù)。錄制好的 trace 文件可以通過 xctrace 導(dǎo)出為 XML 格式的文件,從 XML 文件中讀取到性能數(shù)據(jù)。
值得一提的是,手 Q 作為早期第一批吃螃蟹使用 xctrace 的技術(shù)團(tuán)隊(duì),我們一邊摸著石頭過河探索 workaround,一邊與蘋果團(tuán)隊(duì)合作以提升其可用性。Xcode 14 Release Notes 中解決的多個(gè) issue 也是手 Q 團(tuán)隊(duì)在防劣化開發(fā)過程中向 Apple 提出的:
所以手 Q 團(tuán)隊(duì)已經(jīng)默默幫大家踩過很多坑了!
[2] Xcode Memory Graph:
在排查內(nèi)存泄露相關(guān)問題時(shí),使用 Instruments 內(nèi)存相關(guān)模板只能看到對(duì)象的創(chuàng)建堆棧和引用計(jì)數(shù)的增減過程,無法展示對(duì)象間的引用關(guān)系。面對(duì)這類問題,Xcode Memory Graph 是更好的選擇,但 Xcode Memory Graph 也是一個(gè)嵌入到 Xcode 的 GUI 程序,目前為止還沒有 CLI 實(shí)現(xiàn)。為此,我們調(diào)研了 Xcode Memory Graph 的實(shí)現(xiàn),獲取到相關(guān)協(xié)議,實(shí)現(xiàn)脫離 GUI 生成 Xcode 內(nèi)存圖,并使用 heap、vmmap、leaks 等工具分析內(nèi)存圖,實(shí)現(xiàn)自動(dòng)采集內(nèi)存圖,進(jìn)行大內(nèi)存占用和內(nèi)存泄露的分析和監(jiān)控。
[3] Crash:
Crash 的監(jiān)控比較簡(jiǎn)單,我們是通過檢查測(cè)試過程中設(shè)備上有沒有新生成的 ips 文件方式來監(jiān)測(cè) Crash 的。這種方式的優(yōu)點(diǎn)是監(jiān)控范圍廣,SIGKILL、pre-main 階段 Crash 等常規(guī)方式無法捕獲的 Crash 也能監(jiān)控到。實(shí)踐中遇到的一個(gè)小坑是單臺(tái)設(shè)備每天單個(gè) App 生成 ips 文件是有上限的,之前測(cè)試閾值是 50,崩潰超過 50 次就不再生成 ips 文件。
[4] 高頻日志:
分析客戶端日志是必不可少的排查問題的手段,但大型項(xiàng)目業(yè)務(wù)繁多,會(huì)出現(xiàn)很多無效高頻日志,高頻日志會(huì)占用大量的 IO 和 CPU 資源造成卡頓和發(fā)熱,甚至有導(dǎo)致日志文件過大,無法打撈起來的情況。而且很多時(shí)候,高頻日志的背后就是一段死循環(huán)或者循環(huán)調(diào)用邏輯的存在,所以我們對(duì)高頻日志也進(jìn)行了監(jiān)控和告警提單。
7.1.2)應(yīng)用內(nèi)數(shù)據(jù)采集:
[1] 流量監(jiān)控:
流量下載的數(shù)據(jù)采集,雖然 Instruments Network 模塊能夠監(jiān)控所有的下載請(qǐng)求,但 Network 上顯示的流量大小依賴了 Response Header的 Content-Length 或 Range 字段。由于部分后臺(tái)服務(wù)器并沒有填寫該字段,所以 Instruments 上無法獲取總的下載流量大小,故而放棄 Instruments 上采集數(shù)據(jù),改用 App 運(yùn)行時(shí)收集數(shù)據(jù)。
[2] 業(yè)務(wù)打點(diǎn):
性能數(shù)據(jù)需要和業(yè)務(wù)場(chǎng)景進(jìn)行關(guān)聯(lián),我們采用了蘋果的 Signpost 方案進(jìn)行打點(diǎn)。
選擇 Signpost 打點(diǎn)方案的原因主要是下面 3 點(diǎn):
- 1)和 Instruments 高度契合,Instruments 有 os_signpost 模板,應(yīng)用內(nèi)使用 signpost 相關(guān)接口打的點(diǎn),在 Instruments GUI 展示性能數(shù)據(jù)時(shí),也能將業(yè)務(wù)打點(diǎn)一并展示,方便排查問題;
- 2)signpost 打點(diǎn)數(shù)據(jù)可以使用 xctrace 進(jìn)行導(dǎo)出,可以實(shí)現(xiàn)業(yè)務(wù)場(chǎng)景和性能數(shù)據(jù)的相關(guān)聯(lián);
- 3)相比 print 打點(diǎn)方式,signpost 性能損耗更低。
7.2 靜態(tài)掃描能力
7.2.1)符號(hào)掃描:
平臺(tái)有兩套符號(hào)掃描工具,都是面向鏈接期產(chǎn)物 (Mach-O Image) 進(jìn)行靜態(tài)掃描,分別洞察產(chǎn)物中的 Objective-C (簡(jiǎn)稱 OC) 符號(hào)問題和原生符號(hào)問題。
[1] OC 符號(hào)掃描:
OC 符號(hào)掃描工具,幫助掃描工程產(chǎn)物中存在的 OC Category 同名方法覆蓋和 +load 靜態(tài)初始化方法。
OC Category 同名方法覆蓋是指 Category 機(jī)制隱含的運(yùn)行時(shí)實(shí)現(xiàn)覆蓋問題。
覆蓋問題有兩種情形:
- 1)若主類 (原類) 存在一個(gè)與 Category 擴(kuò)展方法同名的方法,則運(yùn)行時(shí)會(huì)選擇 Category 的實(shí)現(xiàn)使用;
- 2)若存在多個(gè) Category 都對(duì)同一個(gè)類擴(kuò)展了同名的方法,則運(yùn)行時(shí)會(huì)選擇其中一個(gè) Category 的實(shí)現(xiàn)使用。
這兩種情況都可能導(dǎo)致程序邏輯非預(yù)期地調(diào)用到其他庫(kù)的實(shí)現(xiàn),出現(xiàn)功能異常或崩潰。該問題相對(duì)隱蔽不易被察覺,因?yàn)樵阪溄悠陂g不會(huì)產(chǎn)生警告。盡管代碼規(guī)范要求 Category 方法名必須加前綴來規(guī)避該問題,但該問題在大型多源項(xiàng)目的集成過程中,還是時(shí)有發(fā)生,只是往往因?yàn)榍『眉嫒輿]出問題而沒感知。直到某天改動(dòng)后出現(xiàn)莫名異常,溯源后才發(fā)現(xiàn)。
+load 方法在程序加載的靜態(tài)初始化階段執(zhí)行,會(huì)影響應(yīng)用的啟動(dòng)耗時(shí)。
這兩類方法(符號(hào))對(duì)穩(wěn)定性和性能有全局性的影響,因此平臺(tái)建設(shè)了工具來關(guān)注這些符號(hào)。
工具綜合基于 class-dump 和鏈接器生成的 LinkMap 信息 (如果有),獲取產(chǎn)物中的全部 OC 符號(hào)和來源,統(tǒng)計(jì)篩選出重名 Category 方法和 +load 方法。并與 CI 構(gòu)建檢查相結(jié)合,監(jiān)控和管控這兩類問題方法,設(shè)立門禁要求業(yè)務(wù)新引入 +load 和重名方法須拉通基礎(chǔ)側(cè) Review。
[2] 原生符號(hào)掃描:
原生符號(hào)掃描工具,幫助掃描工程所有依賴庫(kù)中存在重復(fù)的庫(kù)函數(shù)(符號(hào)) (主要關(guān)注 C 符號(hào)重復(fù)問題)。
通常重復(fù)的庫(kù)函數(shù)是 C/C++ 編寫的基礎(chǔ)實(shí)用函數(shù),這大部分歸咎于 C/C++ 缺少?gòu)V泛認(rèn)可的依賴管理范式,部分大型業(yè)務(wù)靜態(tài)庫(kù)采取將其依賴的實(shí)用方法庫(kù)也一同編譯打包 (ar) 的范式而導(dǎo)致。這些實(shí)用方法庫(kù)通常是廣泛使用的基礎(chǔ)實(shí)用庫(kù),如 FishHook、zip、libffi 等。若有多個(gè)業(yè)務(wù)靜態(tài)庫(kù)都集成了同源的基礎(chǔ)實(shí)用庫(kù),在鏈接 (ld) 生成可執(zhí)行程序時(shí),鏈接器會(huì)選擇其中一份鏈接 (取決于鏈接先后順序等因素,可以通過 LinkMap 確認(rèn)選用的實(shí)現(xiàn)),它們雖然具有相同的符號(hào) (API),但版本/實(shí)現(xiàn)未必一致、ABI 未必兼容,所以如果鏈接時(shí)選取的實(shí)現(xiàn)不恰當(dāng),則可能出現(xiàn)功能異常或崩潰。
通過原生符號(hào)掃描工具,掃描出重復(fù)的庫(kù)函數(shù),有助于標(biāo)識(shí)出上述這樣“存在多份重復(fù)選其一不兼容”的潛在風(fēng)險(xiǎn)。
工具的工作流程是解析鏈接 (ld) 參數(shù),遍歷每一個(gè)參與鏈接的靜態(tài)庫(kù),使用 nm 工具等工具讀取它們包含的對(duì)外導(dǎo)出 (External & Defined) 符號(hào)。實(shí)踐中集成到 CI,在構(gòu)建完成后的現(xiàn)場(chǎng)回溯構(gòu)建日志取得鏈接 (ld) 參數(shù)并執(zhí)行,統(tǒng)計(jì)出重復(fù)的原生符號(hào)并根據(jù)規(guī)則登記歸檔。
最終的統(tǒng)計(jì)結(jié)果會(huì)展示在 Hodor 平臺(tái),可以查看每個(gè) commit 的重復(fù)符號(hào)變化情況(如下圖所示)。
7.2.2)碰撞掃描:
在 Hodor 防劣化系統(tǒng)上線了一段時(shí)候后,我們抓到了一個(gè)冷啟動(dòng)劣化的 case:主干上一個(gè)新提交導(dǎo)致冷啟動(dòng) T0 階段(pre-main)劣化了 150+ ms,但該提交只修改了一個(gè)方法名。在排除掉外部因素、測(cè)試環(huán)境穩(wěn)定性因素之后,發(fā)現(xiàn)真的因?yàn)橐粋€(gè)方法名導(dǎo)致冷啟動(dòng)劣化那么多。
問題看起來很棘手:幸好,我們有詳細(xì)的 trace 文件,在詳細(xì)分析對(duì)比了劣化前后的堆棧之后,發(fā)現(xiàn)劣化的根源是冷啟動(dòng)創(chuàng)建啟動(dòng)閉包時(shí)調(diào)用了一個(gè) perfect hash 的算法,新修改的方法名導(dǎo)致這個(gè)算法的碰撞次數(shù)增加了,發(fā)生碰撞的情況下,需要 rehash,于是耗時(shí)增加。
以下是生成啟動(dòng)閉包的簡(jiǎn)要流程:
找到了劣化的原因,那如何找到發(fā)生碰撞的方法名呢?
答案只能去 dyld 源碼里找,還好,dyld 是開源的,我們?cè)?dyld 源碼里找到了生成啟動(dòng)閉包相關(guān)的部分,在發(fā)生碰撞的地方輸出對(duì)應(yīng)的 sel 名字,將其編譯起來后,把 QQ 的 Mach-O 掃一遍,這樣我們就找到了所有的碰撞點(diǎn),之后我們修復(fù)了所有的碰撞點(diǎn),冷啟動(dòng)得以降低了 700 ms(iPhone 11)。
我們將該掃描工具部署回了 Hodor 系統(tǒng),監(jiān)測(cè)每一個(gè)提交的碰撞情況,同時(shí)我們也將這個(gè)問題反饋給了蘋果負(fù)責(zé) linker 的團(tuán)隊(duì)。
8、任務(wù)調(diào)度實(shí)現(xiàn)
職責(zé)在于監(jiān)聽 Git 事件與自定義事件并生成多類型的性能測(cè)試任務(wù),在合適的時(shí)間點(diǎn)將任務(wù)與合適的測(cè)試機(jī)進(jìn)行匹配然后生成配置文件,最后將配置文件派發(fā)到數(shù)據(jù)采集端驅(qū)動(dòng)性能測(cè)試任務(wù)在對(duì)應(yīng)測(cè)試機(jī)上執(zhí)行。
8.1任務(wù)類型
防劣化性能測(cè)試任務(wù)主要分為以下幾大類
8.1.1)主流程測(cè)試:
由基礎(chǔ)側(cè)提供的核心測(cè)試用例組,測(cè)試流程包括手Q的幾個(gè)核心場(chǎng)景進(jìn)行測(cè)試(啟動(dòng)、登錄、AIO、頻道、短視頻等),所有分支默認(rèn)運(yùn)行當(dāng)前測(cè)試用例組。
后續(xù)性能報(bào)告也是基于當(dāng)前用例組所上報(bào)的性能數(shù)據(jù)來進(jìn)行對(duì)比。保證統(tǒng)一的測(cè)試用例流程與環(huán)境,性能數(shù)據(jù)的對(duì)比才是可信任的。
8.1.2)專項(xiàng)測(cè)試:
針對(duì)某些性能維度(內(nèi)存、IO、預(yù)下載流量檢測(cè)等)單獨(dú)進(jìn)行測(cè)試。最終生成相應(yīng)性能看板。
8.1.3)自定義用例測(cè)試:
手 Q 功能場(chǎng)景十分的龐大復(fù)雜,基礎(chǔ)用例也無法覆蓋到所有的場(chǎng)景,由此誕生自定義測(cè)試用例功能。
如果業(yè)務(wù)同學(xué)想觀察自己所處業(yè)務(wù)部分詳細(xì)的性能數(shù)據(jù),防劣化系統(tǒng)支持由各業(yè)務(wù)來編寫自定義的測(cè)試用例,測(cè)試完畢后根據(jù)上報(bào)數(shù)據(jù)與定義的場(chǎng)景將自動(dòng)生成相應(yīng)性能看板。
8.1.4)Crash、Monkey 測(cè)試:
在日常開發(fā)中,發(fā)生 Crash 問題將會(huì)嚴(yán)重影響整個(gè)項(xiàng)目開發(fā)進(jìn)度。我們希望能第一時(shí)間將問題檢測(cè)暴露出來并推動(dòng)修改。
啟動(dòng)以及主流程 Crash 則是最為嚴(yán)重的,直接導(dǎo)致項(xiàng)目不可使用,影響大家日常開發(fā)。Master 主干的每一個(gè) Commit 合入都會(huì)進(jìn)行 Crash 測(cè)試。如果發(fā)生 Crash,會(huì)立即拉群通知排查。而對(duì)于非啟動(dòng)以及主流程 Crash 問題則會(huì)進(jìn)行自動(dòng)提單。
而 Monkey 測(cè)試則是模擬用戶操作,無序進(jìn)行操作。能夠盡可能的將 Crash 問題暴露出來。
8.1.5)閑時(shí)利用:
為了更充分的利用防劣化系統(tǒng),在空閑時(shí)間(深夜、周末)會(huì)對(duì)過去已經(jīng)測(cè)試過的主干 Commit 再次進(jìn)行測(cè)試。用盡可能多的測(cè)試來暴露出更多的問題。
8.2 任務(wù)調(diào)度管理
所有生成的測(cè)試任務(wù)會(huì)根據(jù)任務(wù)類型,優(yōu)先級(jí)等條件進(jìn)行一輪排序,最終優(yōu)先保證最緊急的任務(wù)最優(yōu)先執(zhí)行。
簡(jiǎn)單示意圖如下:
當(dāng)任務(wù)狀態(tài)異常時(shí),也會(huì)有告警:
8.3 設(shè)備管理
針對(duì)不同類型的任務(wù)采用不同的策略進(jìn)行測(cè)試機(jī)分配:
- 1)對(duì)于 Crash 任務(wù),為了保證能第一時(shí)間發(fā)現(xiàn)問題,會(huì)分配專門的機(jī)器池進(jìn)行測(cè)試;
- 2)對(duì)于性能任務(wù),根據(jù)版本流程與任務(wù)優(yōu)先級(jí)進(jìn)行動(dòng)態(tài)分配。基礎(chǔ)性能>業(yè)務(wù)自定義>=專項(xiàng)測(cè)試>閑時(shí)利用。
設(shè)備環(huán)境發(fā)生問題,也將及時(shí)進(jìn)行告警:
9、 數(shù)據(jù)處理實(shí)現(xiàn)
9.1概述
由于 Instruments 采集到的性能數(shù)據(jù)量巨大,動(dòng)輒 GB 級(jí)別,無法全量上報(bào),所以性能數(shù)據(jù)采集時(shí)會(huì)進(jìn)行符號(hào)化和性能問題的分析,比如找出卡頓堆棧、內(nèi)存泄露的對(duì)象等。分析完畢后會(huì)將數(shù)據(jù)上報(bào)給服務(wù)端,由服務(wù)端進(jìn)一步處理。
職責(zé)在于將上報(bào)的數(shù)據(jù)根據(jù)不同規(guī)則進(jìn)行計(jì)算存儲(chǔ)。不同維度性能根據(jù)不同規(guī)則計(jì)算,得出相應(yīng)的性能結(jié)果并消費(fèi)劣化性能數(shù)據(jù)(自動(dòng)提單與告警)。
9.2 不同類型性能數(shù)據(jù)的處理
9.2.1) 基礎(chǔ)性能數(shù)據(jù):
對(duì)于基礎(chǔ)性能數(shù)據(jù)而言(CPU、內(nèi)存、IO、線程數(shù)),上報(bào)的數(shù)據(jù)是原始每一次采樣所得數(shù)據(jù)(大致在一秒采樣一次)。
這里誕生了兩種計(jì)算方式:
- 1)對(duì)于關(guān)注整體性能數(shù)據(jù)以及流程比較短的用例,則會(huì)整體計(jì)算出三個(gè)維度的數(shù)據(jù):峰值數(shù)據(jù)、平均數(shù)據(jù)、結(jié)束時(shí)數(shù)據(jù);
- 2)對(duì)于有定義「場(chǎng)景」的用例,會(huì)根據(jù)所傳遞的打點(diǎn)(Signpost)值來找到對(duì)應(yīng)時(shí)間范圍的數(shù)據(jù)進(jìn)行計(jì)算。同樣是以上三個(gè)基礎(chǔ)維度,另外新增一個(gè)耗時(shí)計(jì)算。
整體示意圖如下:
9.2.2)重點(diǎn)性能數(shù)據(jù):
對(duì)于重點(diǎn)關(guān)注的數(shù)據(jù)(啟動(dòng)時(shí)間,啟動(dòng)線程狀態(tài)),采集端會(huì)使用專門的模板來進(jìn)行測(cè)試,上報(bào)數(shù)據(jù)后。Server 端對(duì)多次測(cè)試結(jié)果數(shù)據(jù)進(jìn)行綜合計(jì)算,得出結(jié)果最后展示在相應(yīng)看板上。
9.2.3)自定義性能數(shù)據(jù):
對(duì)于自定義上報(bào)數(shù)據(jù)(重復(fù)符號(hào)變動(dòng),啟動(dòng)階段函數(shù)監(jiān)控),則是開放專門上報(bào)數(shù)據(jù)接口,由對(duì)應(yīng)業(yè)務(wù)方自主計(jì)算上傳(防劣化會(huì)向業(yè)務(wù)方提供基本數(shù)據(jù))。防劣化系統(tǒng)負(fù)責(zé)記錄數(shù)據(jù)并展示相應(yīng)看板。
9.3 消費(fèi)性能劣化數(shù)據(jù)
9.3.1) 自動(dòng)提單:
我們會(huì)定時(shí)掃描數(shù)據(jù)庫(kù)中上報(bào)的性能劣化信息。先根據(jù)白名單以及過濾規(guī)則進(jìn)行篩選,然后將需要提單的數(shù)據(jù)進(jìn)行信息聚合,最終以提單的形式將問題自動(dòng)分配給對(duì)應(yīng)的業(yè)務(wù)負(fù)責(zé)人。
bug 單包含了缺陷的堆棧等詳細(xì)信息:
9.3.2)性能告警:
對(duì)于主干的性能數(shù)據(jù)進(jìn)行實(shí)時(shí)監(jiān)控,不同用例不同性能維度可以配置不同的告警規(guī)則。當(dāng)發(fā)生劣化時(shí)及時(shí)將對(duì)應(yīng)的信息拋到對(duì)應(yīng)業(yè)務(wù)群中進(jìn)行告警。
10、 管理端展示
10.1 防劣化看板
防劣化看板支持查看指定時(shí)間、分支、測(cè)試用例和場(chǎng)景下的每個(gè) commit 的狀態(tài)以及各項(xiàng)性能數(shù)據(jù),并可以快速標(biāo)記 commit,支持與任意 commit 的性能數(shù)據(jù)做對(duì)比。
10.2 分支性能報(bào)告
防劣化系統(tǒng)會(huì)對(duì)所有需求分支的每一次 push 進(jìn)行性能測(cè)試,然后與對(duì)應(yīng)的主干 Commit 性能數(shù)據(jù)進(jìn)行對(duì)比生成性能報(bào)告。能直觀的看到需求分支的性能變化,當(dāng)性能發(fā)生劣化的時(shí)候也能直觀的看到是從哪個(gè) Commit 開始引入劣化問題,方便問題排查。
測(cè)試報(bào)告有多種狀態(tài),比如“等待數(shù)據(jù)上報(bào)”、“自動(dòng)審批通過”、“自動(dòng)審批不通過” 等:
當(dāng)測(cè)試報(bào)告“自動(dòng)審批不通過” 時(shí),也會(huì)標(biāo)注出是哪些指標(biāo)不通過,便于開發(fā)者迅速定位問題:
10.3 測(cè)試用例管理
基礎(chǔ)所提供的主流程測(cè)試用例必然是無法覆蓋手 Q 所有的場(chǎng)景,因此提供開放能力,支持各業(yè)務(wù)方的開發(fā)、測(cè)試自主提供測(cè)試用例。在防劣化平臺(tái)上進(jìn)行配置測(cè)試,測(cè)試完畢后自動(dòng)根據(jù)配置生成相應(yīng)的性能看板。
同時(shí)對(duì)正在運(yùn)行的測(cè)試用例進(jìn)行成功率監(jiān)控,低于一定的成功率將進(jìn)行告警。如業(yè)務(wù)方在一段時(shí)間內(nèi)沒有處理告警,會(huì)將其臨時(shí)下架避免資源浪費(fèi)。
11、 整體架構(gòu)
12、 收益與總結(jié)
Hodor 上線后收益顯著,研發(fā)效率大幅提升!
通過將問題發(fā)現(xiàn)和解決左移到開發(fā)階段,可以有效防止問題漏出到線上導(dǎo)致大盤數(shù)據(jù)劣化。如某次提交導(dǎo)致主干啟動(dòng)耗時(shí)上漲,基于防劣化系統(tǒng)可精準(zhǔn)快速定位到代碼提交者。
Hodor 系統(tǒng)還在不斷迭代中,2024 年還拓展了 QQ 桌面客戶端,并在運(yùn)行效率方面持續(xù)優(yōu)化。目前防劣化系統(tǒng)已經(jīng)落地了 QQ 各平臺(tái)。
防劣化系統(tǒng)從 0 到 1 迭代了將近三年的時(shí)間。從驗(yàn)證可行性跑通最小閉環(huán),到搭建群控機(jī)架一次次為集群擴(kuò)容,實(shí)屬不易。其中涉及到大量的方案討論甚至推翻,很多思路和實(shí)現(xiàn)細(xì)節(jié)是業(yè)界找不到公開方案的,只能自己摸索。
在建設(shè)過程中我們遇到了不少很底層的問題需要與廠商溝通,比如與 Apple 的技術(shù)專家們線上和線下交流過程中也學(xué)到了不少,在此也感謝 Apple。
客戶端的性能穩(wěn)定性防劣化是一個(gè)很復(fù)雜的話題,而且只有體量足夠大的業(yè)務(wù)才會(huì)面臨更多的挑戰(zhàn)。正因?yàn)槲覀兠鎸?duì)的很多問題業(yè)界都無先例可循,所以也期待行業(yè)內(nèi)后續(xù)有更多的分享和交流。同時(shí),我們也期望 Hodor 不僅在性能穩(wěn)定性方面發(fā)揮作用,未來也會(huì)把手 Q 研發(fā)效能的各項(xiàng)指標(biāo)集成進(jìn)來。
13、 相關(guān)資料
[1] 總是被低估,從未被超越,揭秘QQ極致絲滑背后的硬核IM技術(shù)優(yōu)化
[2] 大型IM工程重構(gòu)實(shí)踐:企業(yè)微信Android端的重構(gòu)之路
[3] 企業(yè)微信針對(duì)百萬級(jí)組織架構(gòu)的客戶端性能優(yōu)化實(shí)踐
[4] 微信團(tuán)隊(duì)分享:詳解iOS版微信視頻號(hào)直播中因幀率異常導(dǎo)致的功耗問題
[5] 騰訊技術(shù)分享:Android版手機(jī)QQ的緩存監(jiān)控與優(yōu)化實(shí)踐
[6] 騰訊技術(shù)分享:Android手Q的線程死鎖監(jiān)控系統(tǒng)技術(shù)實(shí)踐
[7] 全面解密新QQ桌面版的Electron內(nèi)存優(yōu)化實(shí)踐
[8] 移動(dòng)端IM實(shí)踐:iOS版微信界面卡頓監(jiān)測(cè)方案
[9] 微信團(tuán)隊(duì)原創(chuàng)分享:Android版微信的臃腫之困與模塊化實(shí)踐之路
[10] 微信Windows端IM消息數(shù)據(jù)庫(kù)的優(yōu)化實(shí)踐:查詢慢、體積大、文件損壞等
[11] 微信團(tuán)隊(duì)分享:微信支付代碼重構(gòu)帶來的移動(dòng)端軟件架構(gòu)上的思考
[12] 微信客戶端團(tuán)隊(duì)負(fù)責(zé)人技術(shù)訪談:如何著手客戶端性能監(jiān)控和優(yōu)化
[13] 抖音技術(shù)分享:飛鴿IM桌面端基于Rust語言進(jìn)行重構(gòu)的技術(shù)選型和實(shí)踐總結(jié)
[14] 阿里技術(shù)分享:閑魚IM基于Flutter的移動(dòng)端跨端改造實(shí)踐
[15] QQ設(shè)計(jì)團(tuán)隊(duì)分享:新版 QQ 8.0 語音消息改版背后的功能設(shè)計(jì)思路
[16] 首次公開,最新手機(jī)QQ客戶端架構(gòu)的技術(shù)演進(jìn)實(shí)踐
14、更多鵝廠技術(shù)文章匯總
微信朋友圈千億訪問量背后的技術(shù)挑戰(zhàn)和實(shí)踐總結(jié)
騰訊技術(shù)分享:騰訊是如何大幅降低帶寬和網(wǎng)絡(luò)流量的(圖片壓縮篇)
IM全文檢索技術(shù)專題(二):微信移動(dòng)端的全文檢索多音字問題解決方案
微信團(tuán)隊(duì)分享:iOS版微信的高性能通用key-value組件技術(shù)實(shí)踐
微信團(tuán)隊(duì)分享:iOS版微信是如何防止特殊字符導(dǎo)致的炸群、APP崩潰的?
微信團(tuán)隊(duì)分享:微信Android版小視頻編碼填過的那些坑
IM全文檢索技術(shù)專題(一):微信移動(dòng)端的全文檢索優(yōu)化之路
企業(yè)微信客戶端中組織架構(gòu)數(shù)據(jù)的同步更新方案優(yōu)化實(shí)戰(zhàn)
微信新一代通信安全解決方案:基于TLS1.3的MMTLS詳解
微信“紅包照片”背后的技術(shù)難題
移動(dòng)端IM實(shí)踐:iOS版微信的多設(shè)備字體適配方案探討
騰訊信鴿技術(shù)分享:百億級(jí)實(shí)時(shí)消息推送的實(shí)戰(zhàn)經(jīng)驗(yàn)
IPv6技術(shù)詳解:基本概念、應(yīng)用現(xiàn)狀、技術(shù)實(shí)踐(上篇)
騰訊技術(shù)分享:GIF動(dòng)圖技術(shù)詳解及手機(jī)QQ動(dòng)態(tài)表情壓縮技術(shù)實(shí)踐
微信團(tuán)隊(duì)分享:Kotlin漸被認(rèn)可,Android版微信的技術(shù)嘗鮮之旅
社交軟件紅包技術(shù)解密(一):全面解密QQ紅包技術(shù)方案——架構(gòu)、技術(shù)實(shí)現(xiàn)等
社交軟件紅包技術(shù)解密(四):微信紅包系統(tǒng)是如何應(yīng)對(duì)高并發(fā)的
社交軟件紅包技術(shù)解密(十):手Q客戶端針對(duì)2020年春節(jié)紅包的技術(shù)實(shí)踐
微信團(tuán)隊(duì)分享:極致優(yōu)化,iOS版微信編譯速度3倍提升的實(shí)踐總結(jié)
IM“掃一掃”功能很好做?看看微信“掃一掃識(shí)物”的完整技術(shù)實(shí)現(xiàn)
微信團(tuán)隊(duì)分享:微信支付代碼重構(gòu)帶來的移動(dòng)端軟件架構(gòu)上的思考
IM開發(fā)寶典:史上最全,微信各種功能參數(shù)和邏輯規(guī)則資料匯總
微信團(tuán)隊(duì)分享:微信直播聊天室單房間1500萬在線的消息架構(gòu)演進(jìn)之路
企業(yè)微信的IM架構(gòu)設(shè)計(jì)揭秘:消息模型、萬人群、已讀回執(zhí)、消息撤回等
IM全文檢索技術(shù)專題(四):微信iOS端的最新全文檢索技術(shù)優(yōu)化實(shí)踐
微信團(tuán)隊(duì)分享:微信后臺(tái)在海量并發(fā)請(qǐng)求下是如何做到不崩潰的
微信Windows端IM消息數(shù)據(jù)庫(kù)的優(yōu)化實(shí)踐:查詢慢、體積大、文件損壞等
微信技術(shù)分享:揭秘微信后臺(tái)安全特征數(shù)據(jù)倉(cāng)庫(kù)的架構(gòu)設(shè)計(jì)
IM跨平臺(tái)技術(shù)學(xué)習(xí)(九):全面解密新QQ桌面版的Electron內(nèi)存優(yōu)化實(shí)踐
企業(yè)微信針對(duì)百萬級(jí)組織架構(gòu)的客戶端性能優(yōu)化實(shí)踐
揭秘企業(yè)微信是如何支持超大規(guī)模IM組織架構(gòu)的——技術(shù)解讀四維關(guān)系鏈
微信團(tuán)隊(duì)分享:詳解iOS版微信視頻號(hào)直播中因幀率異常導(dǎo)致的功耗問題
微信團(tuán)隊(duì)分享:微信后端海量數(shù)據(jù)查詢從1000ms降到100ms的技術(shù)實(shí)踐
大型IM工程重構(gòu)實(shí)踐:企業(yè)微信Android端的重構(gòu)之路
IM技術(shù)干貨:假如你來設(shè)計(jì)微信的群聊,你該怎么設(shè)計(jì)?
微信團(tuán)隊(duì)分享:來看看微信十年前的IM消息收發(fā)架構(gòu),你做到了嗎
長(zhǎng)連接網(wǎng)關(guān)技術(shù)專題(十一):揭秘騰訊公網(wǎng)TGW網(wǎng)關(guān)系統(tǒng)的技術(shù)架構(gòu)演進(jìn)
(本文已同步發(fā)布于:http://www.52im.net/thread-4680-1-1.html)