得物自研移動端弱網診斷工具的技術實踐分享

一、引言

隨着得物用戶規模和業務複雜度不斷提升,端上網絡體驗優化已逐步進入深水區。爲了更好地保障處於弱網狀態下得物 App 用戶的使用體驗,我們在已有的網絡體驗大盤、網絡診斷工具的基礎上研發了弱網診斷能力。該工具能夠高效實時診斷用戶真實網絡環境,同時給出精確網絡質量分級,爲後續 App 各業務場景進行鍼對性優化做好基礎建設保障。

本文將基於得物自研的移動端弱網診斷工具的開發過程,儘可能全面地爲你總結和分享它的具體技術實踐,希望帶給你啓發。

二、本文目錄

1) 引言

2) 網絡性能概念

3) 整體架構

4) 採集層實現 1:HttpRTT 採集

5) 採集層實現 2:吞吐量(throughput)採集

6) 策略層實現

7) 接口層實現

8) 閾值定義

9) 性能指標

10) 應用場景

11) 本文小結

12) 參考資料

13) 得物技術團隊的其它文章

三、網絡性能概念

一些網絡性能指標:

弱網診斷觀察的指標(弱網診斷根據 HttpRTT 和吞吐量來觀察用戶網絡環境):

四、整體架構

本次實現的是被動弱網診斷,也就是不主動發起探測請求,被動採集 App 內的全部網絡請求,再根據一定在策略計算出用戶網絡環境。

相對於主動探測,被動探測不會浪費用戶資源。

尤其是在吞吐量計算方面,主動探測不僅會消耗用戶流量,還可能會對正在進行中的用戶網絡請求產生影響。而且當用戶網絡環境不佳時,負向影響更加嚴重。

以下爲被動網絡診斷的整體架構圖:

五、採集層實現 1:HttpRTT 採集

5.1 概述

HttpRTT 的採集比較簡單,各端根據自身 Http 協議棧的實現獲取到從寫入 requestHeader 開始,到收取 responseHeader 的時間差即可。

對於 Android:我們通過 OkHttp 完成 Http 請求,通過向 OkHttp 註冊網絡監聽即可實現。

需要說明的是:在不修改源碼的情況下,Android 無法獲取到收到第一個響應數據包的時間,只能監聽到 Header 讀取完成,這會有些許誤差,但實測下來可以忽略。

對於其他客戶端內通過自行實現 Http 協議棧發起的網絡請求(如 PCDN),我們通過向其註冊特定的監聽回調,也能獲取到 HttpRTT。

5.2 採集樣本過濾

HttpRTT 包含了服務器處理時間,而當服務器處理時間過長或過低時對其他普遍意義上的 Http 請求參考價值相對較低。

因此我們需要排除這些數據:

六、採集層實現 2:吞吐量(throughput)採集

6.1 概述

throughput(bit/s , bps) = 單位時間內通過的數據量 (bit) / 單位時間 (s)

吞吐量的採集相對要複雜得多,吞吐量採集目的是獲取用戶所能使用的最大的數據率(或帶寬),因而其需要在設備上恰好有大量數據正在傳輸時採集。

對於主動探測來說,在無其他請求干擾的情況下,主動發起一個大數據量 CDN 下載請求即可快速測量出吞吐量。而被動探測則需要想辦法預測或檢測到大量數據傳輸的時刻,並適時計算吞吐量。

6.2 時間窗口

怎樣選取計算吞吐量的時間窗口是計算吞吐量準確性的關鍵。

這個窗口要恰好在大量數據正在傳輸時,不能早也不能晚:

我們知道 Http 請求或多或少會有上行 / 下行數據,但由於服務器處理耗時長短的不確定性(不能算在分母裏),單個 Http 請求測速時並不可靠。

而多個 Http 正在併發請求進行中的時候,其請求的流量會疊加,單個請求的服務器耗時會被其他 Http 請求覆蓋,此時採集吞吐量會是個好的選擇。因此,我們可以在監聽到 Http 併發請求數量達到 5 個以上時採集吞吐量。

時間串口的選擇(忽略建連耗時):

可以看出,當併發較多時,服務器耗時會被覆蓋,每個時間窗口內存在約 4 個 Http 請求的響應數據。

我們始終監聽 App 內的全部 Http 請求,當監控到有 5 個及以上的網絡請求尚未完成時,也就是 Http 併發 5 個以上,開啓時間窗口;當時間窗口已開啓且任意一個請求結束時,我們結束當前時間窗口。

需要注意的是:當我們結束一個時間窗口的時候,需要立刻檢測當前併發是否 5 個以上,而不是等到新的請求到來時,這樣能避免類似上圖的採樣機會被浪費掉,而採樣成功的樣本數越多,越有利於最終結論的準確性(後面策略層會講原因)。

_可行性:_我們的 App 內能滿足 5 個併發以上嗎?

當然可以。通過觀察線下測試和線上數據分析,我們 App 內的併發數能夠滿足吞吐量採集的必要條件。舉個例子,進入商詳一次的併發量就能滿足。

用戶 A 啓動 90 秒內 Http 併發數量(線上數據):

6.3 數據量

分母(單位時間)的問題解決了,分子(單位時間內通過的數據量)如何取值呢?

思路是:

先說思路 1:這個實現上需要我們在時間窗口開始時記錄全部 Http 請求已傳輸的字節數,時間窗口關閉時再記錄一次,然後把當前並行的 Http 請求時間窗口內的數據量全部累加起來。這意爲着我們要時刻監控每一個 Http 請求中每個字節的讀取,成本太高了。另一方面,如果有其他非 Http 請求(或者我們 App 之外的請求)也在進行,我們僅測算 App 內 Http 請求的吞吐量顯然是偏低的。因此,思路 1 不合適。

再說思路 2:相對合適。我們要測算的就是設備的吞吐量。因爲即使是 App 外的流量,其也會有結束的時候,總能爲我們所用(除非用戶對我們 App 單獨做帶寬限制)。

相對於獲取設備內的全部流量,獲取網卡的流量則更爲合適:

需要注意的是系統 API 返回的是字節數(byte),而我們計算的是 bit,因此計算吞吐量時需要進行換算。

6.4 髒數據過濾

前面講到 “併發 5 個時,每個時間窗口內可以採集到約 4 個 Http 請求的響應數據”,然而運氣並不會始終這樣好。

窗口掛起:

如上圖所示:時間窗口 1 內僅兩個有效的 response,時間窗口 2 內僅一個有效的 response,其計算出的吞吐量必然是偏低的。因此,髒數據過濾就顯得十分重要。

1)小數據量過濾:

時間窗口內通過的流量小於 32KB 時,不會產生準確的速率,我們直接忽略這次採樣。要知道,一個圖片的數據量都可能會超過 32KB。

2)低利用率過濾:

低利用率是指由於數據需求較小,導致當前速率遠未達到最大吞吐率的情況。如上圖時間窗口 1,對於一個未被充分利用的網絡,我至少希望一個 HttpRTT 的時間內接收到的數量大於 15KB。

// 窗口是否掛起
fun isHangingWindow(bitsRx: Long, duration: Long): Boolean {
    val kCwndSizeBits = 10 * 1.5 * 1000 * 8
    val multiplier = 1
    val httpRTT = ??? //由Http RTT模塊計算
    val bitsReceivedOverOneHttpRtt = bitsRx * httpRTT / duration
    return bitsReceivedOverOneHttpRtt < kCwndSizeBits * multiplier
}

爲什麼是 15KB ?如果 TCP 連接剛剛建立,由於 Linux 系統的默認設置,客戶端能夠同時發送 10 個數據段,每個數據段時 1460 字節,合計也就是 15KB。

八、參考文獻

[1] 通俗易懂 - 深入理解 TCP 協議(下):RTT、滑動窗口、擁塞處理

http://www.52im.net/thread-515-1-1.html

[2] 現代移動端網絡短連接的優化手段總結:請求速度、弱網適應、安全保障

http://www.52im.net/forum.php?mod=viewthread&tid=1413

[3] 移動端 IM 開發者必讀 (一):通俗易懂,理解移動網絡的“弱” 和“慢”

http://www.52im.net/thread-1587-1-1.html

[4] 全面瞭解移動端 DNS 域名劫持等雜症:原理、根源、HttpDNS 解決方案等

http://www.52im.net/thread-2121-1-1.html

[5] 美圖 App 的移動端 DNS 優化實踐:HTTPS 請求耗時減小近半

http://www.52im.net/thread-2172-1-1.html

[6] 百度 APP 移動端網絡深度優化實踐分享 (三):移動端弱網優化篇

http://www.52im.net/thread-2678-1-1.html

[7] 愛奇藝移動端網絡優化實踐分享:網絡請求成功率優化篇

http://www.52im.net/thread-2981-1-1.html

[8] 美團點評的移動端網絡優化實踐:大幅提升連接成功率、速度等

http://www.52im.net/thread-3015-1-1.html

[9] IM 開發者的零基礎通信技術入門 (十二):上網卡頓?網絡掉線?一文即懂!

http://www.52im.net/thread-2406-1-1.html

[10] 淘寶移動端統一網絡庫的架構演進和弱網優化技術實踐

http://www.52im.net/thread-4470-1-1.html

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/kBSuqXAaWNNDZCcoiDADBA