百度 Android 直播秒開體驗優化
作者 | 任雪龍
導讀
網路直播功能作為一項網際網路基本能力已經越來越重要,手機中的直播功能也越來越完善,電商直播、新聞直播、娛樂直播等多種直播型別為使用者提供了豐富的直播內容。隨著直播的普及,為使用者提供極速、流暢的直播觀看體驗也越來越重要。
全文6657字,預計閱讀時間17分鐘。
01 背景
百度 APP 作為百度的航母級應用為使用者提供了完善的移動端服務,直播也作為其中一個必要功能為使用者提供內容。隨著直播間架構、業務能力逐漸成熟,直播間播放指標優化也越來越重要。使用者點選直播資源時,可以快速的看到直播畫面是其中一個核心體驗,起播速度也就成了直播間優化中的一個關鍵指標。
02 現狀
由於包體積等原因,百度 APP 的 Android 版中直播功能使用外掛方式接入,在使用者真正使用直播功能時才會將直播模組載入。為解決使用者點選直播功能時需要等待外掛下載、安裝、載入等階段及相容外掛下載失敗的情況,直播團隊將播放、IM 等核心能力抽到了一個獨立的體積較小的一級外掛並內建在百度 APP 中,直播間的掛件、禮物、關注、點贊等業務能力在另外一個體積較大的二級外掛中。特殊的外掛邏輯和複雜的業務場景使得 Android 版整體起播時長指標表現的不盡人意。
2022 年 Q1 直播間整體起播時長指標 80 分位在 3s 左右,其中二跳(直播間內上下滑)場景在 1s 左右,外掛拆分上線後通過觀察起播資料發現隨著版本收斂,一跳進入直播間攜帶流地址(頁面啟動後會使用該地址預起播,與直播列表載入同步執行)場景起播時有明顯的增長,從發版本初期 1.5s 左右,隨版本收斂兩週內會逐步增長到 2.5s+。也就是線上在直播間外點選直播資源進直播間時有很大一部分使用者在點選後還需要等待 3s 甚至更長時間才能真正看到直播畫面。這個時長對使用者使用直播功能有非常大的負向影響,起播時長指標急需優化。
03 目標
△起播鏈路
起播過程簡單描述就是使用者點選直播資源,開啟直播頁面,請求起播地址,呼叫核心起播,核心起播完成,核心通知業務,業務起播完成打點。從對核心起播時長監控來看,直播資源的在核心中起播耗時大約為 600-700ms,考慮鏈路中其他階段損耗以及二跳(直播間內上下滑)場景可以在滑動時提前起播,整體起播時長目標定位為1.5 秒;考慮到有些進入直播間的位置已經有了起播流地址,可以在某些場景省去 “請求起播地址” 這一個階段,在這種直播間外已經獲取到起播地址場景,起播時長目標定為 1.1 秒。
04 難點
特殊的外掛邏輯和複雜的業務場景使得 Android 版每一次進入直播的起播鏈路都不會完全一樣。只有一級外掛且二級外掛還未就緒時在一級外掛中請求直播資料並起播,一二級外掛都已載入時使用二級外掛請求直播資料並處理起播,進直播間攜帶流地址時為實現秒開在 Activity 啟動後就建立播放器使用直播間外攜帶的流地址起播。除了這幾種鏈路,還有一些其他情況。複雜的起播鏈路就導致了,雖然在起播過程中主要節點間都有時間戳打點,也有天級別相鄰兩個節點耗時 80 分位報表,但線上不同場景上報的起播鏈路無法窮舉,使用現有報表無法分析直播大盤起播鏈路中真正耗時位置。需要建立新的監控方案,找到耗時點,才能設計針對性方案將各個耗時位置進行優化。
05 解決方案
5.1 設計新報表,定位耗時點
△一跳有起播地址時起播鏈路簡圖
由於現有報表無法滿足起播鏈路耗時階段定位,需要設計新的監控方案。觀察在開啟直播間時有流地址場景的流程圖(上圖),進入直播間後就會同步建立直播間列表及建立播放器預起播,當直播間列表建立完畢且播放器收到首幀通知時起播流程結束。雖然使用者點選到頁面 Activity 的 onCreate 中可能有多個節點(一級外掛安裝、載入等),頁面 onCreate 呼叫播放器預起播中可能多個節點,核心完成到直播業務收到通知中有多個節點,導致整個起播鏈路無法窮舉。但是我們可以發現,從使用者點選到 onCreate 這個路徑是肯定會有的,onCreate 到建立播放器路徑也是肯定有的。這樣就說明雖然兩個關鍵節點間的節點數量和鏈路無法確定,但是兩個關鍵節點的先後順序是一定的,也是必定會有的。由此,我們可以設計一個自定義鏈路起點和自定義鏈路終點的查詢報表,通過終點和起點時間戳求差得到兩個任意節點間耗時,將線上這兩個節點所有差值求 80 分位,就可以得到線上起播耗時中這兩個節點間耗時。將起播鏈路中所有核心關鍵節點計算耗時,就可以找到整個起播鏈路中有異常耗時的分段。
按照上面的思路開發新報表後,上面的鏈路各階段耗時也就比較清晰了,見下圖,這樣我們就可以針對不同階段逐個擊破。
△關鍵節點間耗時
5.2 一跳使用一級外掛起播
使用新報表統計的重點節點間耗時觀察到,直播間列表建立(模版元件建立)到真正呼叫起播(業務檢視就緒)中間耗時較長,且這個耗時隨著版本收斂會逐步增加,兩週內大約增加 1000ms,首先我們解決這兩個節點間耗時增加問題。
經過起播鏈路觀察和分析後,發現隨版本收斂,這部分起播鏈路有較大變化,主要是因為隨版本收斂,在二級外掛中觸發 “業務呼叫起播” 這個節點的佔比增加。版本收斂期,進入直播間時大概率二級外掛還未下載就緒或未安裝,此時一級外掛中可以很快的進行列表建立並建立業務檢視,一級外掛中在 RecyclerView 的 item attach 到檢視樹時就會觸發起播,這個鏈路主要是等待核心完成首幀資料的拉取和解析。當二級外掛逐漸收斂,進入直播間後一級外掛就不再建立業務檢視,而是有二級外掛建立業務檢視。由於二級外掛中業務元件較多逐個載入需要耗時還有一級到二級中逐層呼叫或事件分發也存在一定耗時,這樣二級外掛起播場景就大大增加了直播間列表建立(模版元件建立)到真正呼叫起播(業務檢視就緒)中間耗時。
5.2.1 一跳全部使用一級外掛起播
基於上面的問題分析,我們修改了一跳場景起播邏輯,一跳全部使用一級外掛起播。一級外掛和二級外掛建立的播放器父容器 id 是相同的,這樣在一級外掛中初始化播放器父容器後,當核心首幀回撥時起播過程就可以結束了。二級外掛中在初始化播放器父容器時也會通過 id 判斷是否已經新增到檢視樹,只有在未新增的情況(二跳場景或一跳時出現異常)才會在二級中進行兜底處理。在一級外掛中處理時速度可以更快,一級優先二級兜底邏輯保證了進入直播間後一定可以順利初始化檢視。
5.2.2 提前請求介面
使用由一起外掛處理起播優化了二級外掛鏈路層級較多問題,還有一個耗時點就是進直播間時只傳入了房間 room_id 未攜帶流地址場景,此時需要通過介面請求獲取起播資料後才能建立播放器和起播。為優化這部分耗時,我們設計了一個直播間資料請求管理器,提供了快取資料和超時清理邏輯。在頁面 onCreate 時就會觸發管理器進行介面請求,直播間模版建立完成後會通過管理器獲取已經請求到的直播資料,如果管理器介面請求還未結束,則會複用進行中請求,待請求結束後立刻返回資料。這樣在進直播間未攜帶流資料時我們可以充分利用圖中這 300ms 時間做更多必要的邏輯。
5.3 播放器Activity外預起播
通過進直播間播放器預建立、預起播、一跳使用一級外掛起播等方案來優化進入直播間業務鏈路耗時後,業務鏈路耗時逐漸低於核心部分耗時,播放器核心耗時逐漸成為一跳起播耗時優化瓶頸。除了在核心內部探索優化方案,繼續優化業務整個起播鏈路也是一個重要方向。通過節點間耗時可以發現,使用者點選到 Activity 頁面 onCrete 中間也是有 300ms 左右耗時的。當無法將這部分耗時縮到更短時,我們可以嘗試在這段時間並行處理一些事情,減少頁面啟動後的部分邏輯。
一級外掛在百度 APP 中內建後,設計並上線了外掛預載入功能,上線後用戶通過點選直播資源進入直播間的場景中,有 99%+ 佔比都是直播一級外掛已載入情況,一級外掛載入這裡就沒有了更多可以的操作空間。但將預起播時機提前到使用者點選處,可以將核心資料載入和直播間啟動更大程度並行,這樣來降低核心耗時對整個起播耗時影響。
△播放器在直播間外起播示意圖
如上圖,新增一個提前起播模組,在使用者點選後與頁面啟動並行建立播放器起播並快取,頁面啟動後建立播放器時會先從提前起播模組的快取中嘗試取已起播播放器,如果未獲取到則走正常播放器建立起播邏輯,如果獲取到快取的播放器且播放器未發生錯誤,則只需要等待核心首幀即可。
播放器提前起播後首幀事件大概率在 Activity 啟動後到達,但仍有機率會早於直播業務中設定首幀監聽前到達,所以在直播間中使用複用核心的播放器時需要判斷是否起播成功,如果已經起播成功需要馬上分發已起播成功事件(含義區別於首幀事件,防止與首幀事件混淆)。
提前起播模組中還設計了超時回收邏輯,如果提前起播失敗或 5s (暫定)內沒有被業務複用(Activity 啟動異常或其他業務異常),則主動回收快取的播放器,防止直播間沒有複用成功時提前建立的播放器佔用較多記憶體及避免洩漏;超時時間是根據線上大盤起播時間決定,使用一個較大盤起播時間 80 分位稍高的值,防止起播還未完成時被回收,但也不能設定較長,防止不會被複用時記憶體佔用較多。
通過提前起播功能,實驗期命中提前起播邏輯較不進行提前起播邏輯,整體起播耗時 80 分位優化均值:450ms+。
5.4直播間任務打散
△核心首幀分發耗時
業務鏈路和核心鏈路耗時都有一定優化後,我們繼續拆解重點節點間耗時。核心內部標記首幀通知到直播業務真正收到首幀通知之間耗時較長,如上圖,線上核心首幀分發耗時 80 分位均值超過 1s,該分段對整體起播耗時優化影響較大。核心首幀是在子執行緒進行標記,通知業務時會通過主執行緒 Handler 分發訊息,通過系統的訊息分發機制將事件轉到主執行緒。
通過排查核心標記首幀時間點到業務收到首幀通知事件時間點之間所有主執行緒任務,發現在首幀分發任務開始排隊時,主執行緒任務佇列中已有較多其他任務,其他事件處理時間較長,導致首幀分發排隊時間較久,分發任務整體耗時也就較長。直播業務複雜度較高,如果核心首幀分發任務排隊時直播間其他任務已在佇列中或正在執行,首幀分發任務需要等直播任務執行完成後才能執行。
通過將直播間啟動過程中所有主執行緒任務進行篩查,發現二級外掛的中業務功能較多,整體載入任務執行時間較長,為驗證線上也是由於二級業務任務阻塞了首幀分發任務,我們設計了一個二級元件載入需要等待核心首幀後才能進行的實驗,通過實驗組與對照組資料對比,在命中實驗時首幀分發耗時和起播整體耗時全部都有明顯下降,整體耗時有 500ms 左右優化。
通過實驗驗證及本地對起播階段業務邏輯分析,定位到直播間各業務元件及對應檢視的預載入數量較多且耗時比較明顯,這個功能是二級外掛為充分利用直播間介面資料返回前時間,二級外掛載入後會與介面請求並行提前建立業務檢視,提起初始化元件及檢視為介面完成後元件渲染節省時間。如果不預建立,介面資料回來後初始化業務元件也會主動建立後設置資料。但將所有預建立任務全部序列執行耗時較長,會阻塞主執行緒,頁面一幀中執行太多工,也會造成頁面明顯示卡頓。
發現這個阻塞問題後,我們設計了將預建立檢視任務進行拆分打散,將一起執行的大任務拆分成多個小任務,每個元件的初始化都作為一個單獨任務在主執行緒任務佇列中進行排隊等待執行。避免了一個大任務耗時特別長的問題。該功能上線後,整個二級外掛中的元件載入大任務耗時降低了 40%+。
5.5 核心子執行緒分發首幀
由於主執行緒訊息佇列中任務是排隊執行的,將阻塞首幀分發事件的大任務拆分成較多小任務後,還是無法解決首幀事件開始排隊時這些小任務已經在主執行緒任務佇列中排隊問題。除了降低直播業務影響,還可以通過加快核心任務分發速度,使首幀分發耗時降低。需要設計一個在不影響核心穩定性與業務邏輯情況下核心首幀事件如何避免主執行緒排隊或快速排隊後被執行的方案。
為解決上面的問題, 我們推動核心,單獨增加了一個子執行緒通知業務首幀事件能力。業務收到子執行緒中首幀回撥後通過 Handler 的 postAtFrontOfQueue() 方法將一個新任務插到主執行緒任務佇列最前面,這樣主執行緒處理完當前任務後就可以馬上處理我們新建的這個任務,在這個新任務中可以馬上處理播放器上屏邏輯。無需等待播放核心原本的主執行緒訊息。
主執行緒任務前插無法打斷新任務排隊時主執行緒中已經開始執行的任務,需要正在執行任務結束後才會被執行。為優化這個場景,核心通過子執行緒通知首幀後,播放器中需要記錄這個狀態,在一級外掛及二級外掛中的直播間業務任務執行開始前後,增加判斷播放器中是否已經收到首幀邏輯,如果已經收到,就可以先處理上屏後再繼續當前任務。
通過直播核心首幀訊息在主執行緒任務佇列前插和業務關鍵節點增加是否可上屏判斷,就可以較快處理首幀通知,降低首幀分發對起播時長影響。
5.6 起播與完載指標平衡
直播間起播優化過程中,完載時長指標(完載時長:使用者點選到直播間核心功能全部出現的時間,其中經歷頁面啟動,直播間列表建立,二級外掛下載、安裝、載入,直播間介面資料請求,初始化直播間功能元件檢視及渲染資料,核心業務元件顯示等階段)的優化也在持續進行。直播間二級外掛是在使用二級外掛中的功能時才會觸發下載安裝及載入邏輯,完載鏈路中也注意到了使用者點選到頁面 onCreate 這段耗時,見下圖。
△頁面啟動耗時示意圖
為優化直播間完載指標,直播團隊考慮如果將外掛載入與頁面啟動並行,那麼完載耗時也會有一定的優化。直播團隊繼續設計了二級外掛預載入方案,將二級外掛載入位置提前到了使用者點選的時候(該功能上線在 5.4、5.5 章節對應功能前)。該功能上線後試驗組與對照組資料顯示,實驗組完載耗時較對照組確實有 300ms+ 優化。但起播耗時卻出現了異常,實驗組的起播耗時明顯比對照組增長了 500ms+,且隨版本收斂這個起播劣化還在增加。我們馬上很快發現了這個異常,並通過資料分析確定了這個資料是正確的。完載的優化時如何引起起播變化的?
經過資料分析,我們發現起播受影響的主要位置還是核心首幀訊息分發到主執行緒這個分段引起,也就是二級外掛載入越早,核心首幀分發與二級元件載入時的耗時任務衝突可能性越大。確認問題原因後,我們做了 5.4、5.5 章節的功能來降低二級元件載入任務對起播影響。由於二級外掛中的耗時任務完全拆分打散來緩解二級外掛預下載帶來的起播劣化方案複雜度較高,對直播間邏輯侵入太大,二級外掛提前載入沒有完全上線,完載的優化我們設計了其他方案來實現目標。
雖然不能在進入直播間時直接載入二級外掛,但我們可以在進入直播間前儘量將二級外掛下載下來,使用時直接載入即可,這個耗時相對下載耗時是非常小的。我們優化了外掛預下載模組,在直播間外展示直播資源時觸發該模組預下載外掛。該模組會通過對當前裝置網路、頻寬、下載頻次等條件綜合判斷,在合適的時機將匹配的二級外掛進行下載,外掛提前下載後對完載指標有較大優化。除了外掛預下載,直播間內通過 5.4 章節直播間二級元件初始化拆分,也將全部元件初始化對主執行緒阻塞進行了優化,這樣介面資料請求成功後可以優先處理影響完載統計的元件,其他元件可以在完載結束後再進行初始化,這個方案也對直播完載指標有明顯優化。
除了以上兩個優化方案,直播團隊還在其他多個方向對完載指標進行了優化,同時也處理了完載時長與起播時長的指標平衡,沒有因為一個指標優化而對其他指標造成劣化影響。最終實現了起播、完載指標全部達到目標。
06 收益
△2022 Android 端起播耗時走勢
經過以上多個優化方案逐步迭代,目前 Android 端最新版本資料,大盤起播時間已經由 3s+ 降到 1.3s 左右;一跳帶流地址時起播時長由 2.5s+ 左右降低到 1s 以內;二跳起播時長由 1s+ 降低到 700ms 以內,成功完成了預定目標。
07 展望
起播時長作為直播功能一個核心指標,還需要不斷打磨和優化。除了業務架構上的優化,還有優化拉流協議、優化緩衝配置、自適應網速起播、優化 gop 配置、邊緣節點加速等多個方向可以探索。百度直播團隊也會持續深耕直播技術,為使用者帶來越來越好的直播體驗。
——END——
推薦閱讀:
- 精準水位在流批一體資料倉庫的探索和實踐
- 視訊編輯場景下的文字模版技術方案
- 淺談活動場景下的圖演算法在反作弊應用
- 百度工程師帶你玩轉正則
- Serverless:基於個性化服務畫像的彈性伸縮實踐
- 百度APP iOS端記憶體優化-原理篇
- 從稀疏表徵出發、召回方向的前沿探索
- 效能平臺數據提速之路
- 採編式AIGC視訊生產流程編排實踐
- 百度工程師漫談視訊理解
- PGLBox 超大規模 GPU 端對端圖學習訓練框架正式釋出
- 百度工程師淺談分散式日誌
- 百度工程師帶你瞭解Module Federation
- 巧用Golang泛型,簡化程式碼編寫
- Go語言DDD實戰初級篇
- 百度工程師帶你玩轉正則
- Diffie-Hellman金鑰協商演算法探究
- 貼吧低程式碼高效能規則引擎設計
- 淺談許可權系統在多利熊業務應用
- 分散式系統關鍵路徑延遲分析實踐