解密Lego:客戶端日志系統的演進
引言
埋點對于移動應用來說至關重要,無論是賦能業務增長,還是優化技術實現,埋點數據和技術日志都為決策和優化提供了關鍵依據。轉轉App也有著一套自研的日志采集系統(Lego),從2015年轉轉App上線第一個版本到現在,Lego逐步從一個單一功能架構演變為支持自動化采集、實時上報、業務與技術日志隔離的復合架構。
Lego演進歷程
總結起來,可以分為四個階段:
- 從零到一建設能力;
- 業務埋點與技術日志拆分;
- 提高核心埋點實時性;
- 架構升級性能提升。
本文將為大家介紹客戶端在Lego升級的四個階段中的設計思路和解決方案。
Lego:從零到一
背景
- 移動開發早期(2015 年)階段,移動設備資源(CPU,內存,電量)有限,網絡環境不穩定(網速慢,連接質量差);
- 業界技術實現普遍注重低功耗,低開銷實現數據的采集上報;
- 埋點相對較少,業務規模相對較小;
架構設計
在上面的背景下,首先面對不穩定的網絡環境,采用合并多個埋點的數據,將數據寫入本地文件的方式,盡量減少接口請求的頻次,防止網絡傳輸不穩定導致數據丟失,較低的上報頻次,同時也減輕服務端的壓力;并且將埋點數據文件壓縮處理后再上報以節省網絡傳輸的數據流量開銷。其次由于應用主進程運行內存有限,又涉及數據格式化,文件寫入,文件壓縮,網絡接口上報等操作,采用獨立子進程的方式處理可以避免擠占主進程的資源,同時隔離影響,由此有了下面的架構設計:
Lego初始架構
圖中可見,應用主進程通過啟動Service(后臺服務)實現與子進程數據通信,發送配置更新,寫日志和上傳日志的指令和數據到 Lego 子進程。然后在子進程中進行初始化配置,將日志數據格式化寫入文件,在動態配置的時間間隔(默認兩分鐘)或者接收到主動觸發的上傳指令時,將日志文件壓縮后通過接口上傳,成功后則刪除壓縮文件,否則在下次發送時機觸發時,再次嘗試上傳。
架構特點
- 獨立內存空間:采用子進程做日志處理上報,子進程擁有獨立的內存空間,避免擠占應用主進程的內存等資源;
- 進程隔離,穩定性高:子進程獨立與主進程運行,如果子進程在執行日志寫入或上傳時崩潰,不會影響App主進程的正常運行。這樣大大提高應用的穩定性與用戶體驗;
- 性能開銷低:子進程中將日志數據寫入文件,每兩分鐘合并發送一次,可以減少頻繁的接口請求,減輕服務端壓力的同時減少應用的性能開銷。
- 節省流量:客戶端經過格式化數據,寫入本地文件后,每兩分鐘將日志文件壓縮后上傳到數據服務端,服務端接收數據后再進行文件解壓縮,落盤,清洗,落表。
Lego4APM:業務埋點與技術日志拆分
背景
- 用戶規模增長,精細化和自動化決策運營的內在需求,迫切需要強實時性的數據采集,上報,處理;
- 業務埋點與APM 日志混合上報,未做區分,給大數據部門的數據處理造成時效性壓力;
- 客戶端本身也需要將性能相關的埋點統一化,規范化,需要有新的聚合維度;
實現方案
上報的實現還是采用 Lego 的原有架構,復制了新的組件 Lego4APM,將上報的數據服務接口換成技術埋點的專用接口,將技術日志與業務埋點分流。按照日志級別劃分,聚合原有的性能日志埋點,再將原有的日志采集接口的底層上報邏輯直接遷移到新組件。
拆分后結構圖
Lego業務與技術埋點拆分結構圖
圖中ZPM為基于轉轉位置模型自研的自動化埋點采集框架,采集完的數據通過 Lego 上報,APM則是性能監控框架采集技術日志,通過Lego4APM組件上報,其中 Lego 與 Lego4APM 是相同實現的兩套獨立的組件。
LegoRealtime:提高實時性
背景
將用戶行為埋點與 APM 日志通過 Lego 和 Lego4Apm 分流后,做到數據隔離,但是Lego 原有設計是多埋點合并上報,每2 分鐘壓縮文件后上報一次,為實現自動化運營的整體方案,客戶端要盡可能保證在 200ms 時延內完成核心用戶行為的采集上報,提高實時性。
方案設計
方案設計階段我們也調研前端的方案,是直接采用的接口上報,但是作為移動端,需要發揮移動端的特性,下面是實時版本設計思路。
LegoRealtime 架構圖
方案實現關鍵
- 數據備份容災:考慮移動端的環境多樣問題,容易出現了異常,弱網,斷網的情況,數據備份很必要,同時也要區分日志是實時的,還是異常備份數據,涉及日志記錄的狀態修改,數據庫備份比文件備份更合適。
- 異常數據重傳機制:在網絡連接恢復,應用重新啟動,以及固定時間間隔,對異常數據進行批量重傳;
- 簡單格式化,高效處理:實時上傳只利用了https本身支持的內容壓縮,未做額外的壓縮等處理,直接以json格式上傳數據,數據服務端在處理數據時相比壓縮格式上報,不需要額外的解壓縮處理,更高效。
- 穩定可控:方案設計之初就考慮上線后如何控制穩定遷移以及數據驗證的問題,增加了灰度控制開關和數據驗證接口。
遷移方案
- 無侵入遷移:為了避免大量更改原有業務埋點的接入代碼,我們選擇通過攔截原有 Lego 上報的接口,通過白名單控制核心埋點走實時上報,對原有接入業務無侵入,降低影響范圍。
- 上線穩定控制:配置 ABTest灰度開關,方案驗證階段,先按照10%的灰度比例切換到實時上報,觀察數據完整性,后端服務的穩定性,驗證通過后逐步增大比例到 50%,穩定運行一周后全量切換。
- 數據質量驗證:
- 時效性方面,通過 APM 后臺的監控數據,確認Lego 實時上報接口的往返時延是否達到低時延設計目標。
- 完整性方面,采用了實時埋點通過實時方式和文件壓縮合并方式同時上報的方案來驗證數據完整性,其中文件壓縮合并方式使用的是Lego4APM,因為技術日志落盤數據與業務數據是隔離的,將這部分數據上報到技術埋點不會污染業務數據,用以驗證完整性非常合適。
驗證結論
- 性能平臺監控到,實時上報接口的平均往返時延為 160ms,發送時延按一半估算為80ms,遠遠低于設計目標的200ms,為大數據端數據處理預留了操作空間;
- 由于實現的機制不同,Lego實時上報的數據比 Lego4APM 上報的數據多 1%,在合理范圍內;
Lego 新架構:性能提升
背景
- 業務方面:
- 某些場景存在后臺啟動服務,存在隱私合規問題;
- 數據組反饋數據重復上報問題,以及埋點缺失問題;
- 技術方面:
- 應用市場反饋 Lego 相關的卡頓問題(當埋點量較多時,頻繁從主進程發送埋點數據和配置到子進程,每次數據交互都是 IPC,這會擠占系統的 binder 通信的資源,Android系統的各種服務包括頁面的創建,點擊事件的交互等都是使用 binder 通信實現,容易引發卡頓問題)
- 多進程配置管理復雜,相同的配置主進程配置后需要再同步至子進程,這給組件的升級維護帶來了額外的成本。
- Lego 和 Lego4APM相同功能的多套代碼,修改出現分叉,維護成本增高;
- Lego 項目由來已久,實現方案不符合于移動端內存相對充足,性能過剩的現狀;
新方案架構設計
基于業務和技術多方面因素考慮,我們對 Lego進行了重新設計,新架構如下圖:
Lego 新架構設計
圖中可以看到,新架構采用的是子線程方案,在主進程創建Lego 實例,通過實例進行配置管理,寫日志和上傳觸發, 同時實例通過創建HandlerThread 子線程,在子線程中進行日志文件寫入和壓縮上傳。
新方案優點
- 無需IPC,開銷低:老架構的子進程方案需要頻繁的 IPC,且更改配置時,主進程和子進程都要維護,而新架構在同一進程中,線程間內存共享,無需要 IPC的額外性能消耗,卡頓問題自然解決,配置只需要保證線程安全即可。
- 無隱私合規問題:老架構某些場景觸發后臺啟動服務實現IPC,而上報數據包含位置信息,用戶設備 id等隱私數據,引發隱私不合規問題,新架構中無需IPC,也就不需要從后臺啟動服務了。
- 支持多實例,易維護:非核心用戶行為埋點和 APM 埋點的上報實現基本一致,老架構需要多份復制,額外占用系統進程,同時維護成本大,新架構支持多個實例后,兩種上報只需要兩個實例,兩套配置即可,性能和維護成本上沒有額外的消耗。
- 上報穩定:老架構存在重復上報,埋點缺失的問題,新架構中通過 HandlerThread 的事件循環隊列實現寫日志與上傳日志串行執行。防止邊上傳邊寫入容易造成重復上報和缺失的問題。
版本遷移方案
Lego、Lego4APM作為客戶端的基礎能力,承載著客戶端所有業務及技術埋點,一旦出現問題,對于客戶端來說將是災難。對如此重要的組件進行重構,面臨巨大的風險,需要制定一個穩妥的、漸進的、對上層業務無感知的遷移方案。計劃將版本遷移拆分為兩個階段:
- 階段一:對照驗證,通過選定指定埋點,對比新老版本埋點數量及參數是否在合理范圍內,驗證時間全量后兩天,驗證通過進入第二階段,驗證不通過則停止版本遷移,定位問題后下個版本繼續進入階段一。
- 階段二:版本切換,按用戶比例逐步切換到新版Lego,用戶比分為三個階梯:10%、50%、100%,每個階梯灰度一周時間,期間觀察核心業務埋點指標(如登錄PV/UV、詳情頁PV/UV、訂單PV/UV)是否正常。
遷移后埋點數據對比結果
驗證結論:新版本埋點數據的內容完整沒有缺失,并且比舊版本的數據量多1%
總結與展望
Lego系統結構
上圖是目前轉轉客戶端的Lego日志系統結構圖,其中包含了數據采集和數據上報兩個部分。業務核心埋點數據由ZPM(轉轉位置模型自動化采集框架)采集,通過LegoRealtime組件實時上報;業務其他埋點則是手動埋點采集,通過基于Lego新架構的Lego4Buz實例上報;技術日志用APM日志框架(性能監控)采集,由基于Lego新架構的Lego4APM實例上報。
通過這些優化和升級,不僅增加數據上報的可靠性和實時性,還顯著提升了穩定性,降低了維護成本。在這個過程中也收獲了寶貴的經驗,在實現日志上報功能時,我們不僅需要考慮如何減少不必要的性能開銷,發揮客戶端的優勢,合并上報以減輕服務器的壓力,同時我們還需要確保新舊版本的平滑過渡和安全遷移,制定周全的遷移方案,以便在出現問題時能夠實時回退,做到安全可控。
關于埋點上報的優化,我們還有其他的演進方向,如LegoRealtime實時上報,頻繁的https接口請求,可以替換成其他低功耗傳輸方式;而Lego新架構的異常處理方面,在服務異常時會頻繁重試,可能會讓系統異常問題雪上加霜。此外,大日志文件上傳的策略優化等方面,還有演進升級的空間,如采取拋棄策略,避免影響整體運行,或者拆分成小文件縮短上報時間,提高成功率。
總之,升級之路是不斷優化和改進現有方案的過程,我們將繼續尋求更加高效、穩定和安全的實現方式,推動Lego系統的不斷演進和升級。