百度App網絡深度優化系列《三》弱網優化

本文是百度網絡優化系列文章的終結篇,該文深入探討了網絡優化中最爲複雜且難驗證和分析的問題——弱網問題。

前言

網絡優化解決的核心問題有三個,第一是安全問題,我們在系列《一》DNS優化進行了詳細的講解。第二是速度問題,我們系列《二》連接優化也做了詳細的介紹。第三是弱網問題,它是網絡優化中最爲複雜且需要反覆驗證和分析的問題,我們的系列《三》弱網優化就是要深入探討這個問題。

背景

弱網優化需要解決的核心問題有兩點:

  • 移動網絡環境如此複雜,我們如何確定當下就是弱網環境

  • 確定爲弱網環境下,我們如何提升弱網下的成功率,降低弱網下的時延,進而提升用戶的網絡體驗

百度App承載着億級流量,弱網比例0.95%,可謂不小,這個比例是如何得來的呢?還是要從什麼是判斷弱網指標說起。

判斷弱網的指標

首先我們來探討下都有哪些指標會影響到網絡的質量,包括httprtt,tcprtt,throughput,signal strength,bandwidth-delay product。

影響網絡質量的指標

1.httprtt

httprtt(http Round-Trip Time)又名TTFB(Time to first byte),指從客戶端請求的第一個字節開始發送到接收到http header的第一個字節的時間差。httprtt的時間如果過長,一方面是客戶端本身接入網絡質量的問題,另一方面是服務的延時比較大。

2.tcprtt

tcprtt(tcp Round-Trip Time)指客戶端tcp信道第一個字節發送到接收第一個字節的時間差。因爲HTTP協議底層是基於TCP的,所以在複用同一條tcp連接的前提下,httprtt的時間是包含tcprtt的時間的。大部分情況下httprtt已經可以說明問題的原因。

3.throughput

throughput,中文名字吞吐量,它是用來衡量單位時間內成功傳送數據的數量,是可以比較客觀的衡量網絡質量的指標。吞吐量 =(獲bits結束大小 - 獲bits開始大小)/(獲bits結束時間 - 獲bits開始時間),這裏有個細節需要注意,posix socket的read函數返回值是bytes,所以要乘以8得到bits。通常在httprtt比較小的情況下,網絡依然很慢,這個時候就可以使用吞吐量來確定網絡的質量。

4.signal strength

signal strength,這裏指的是無線信號強度,在Android上可以通過PhoneStateListener的onSignalStrengthsChanged方法獲取到信號強弱,但要注意只能在Android M以上的版本才生效。iOS上暫時沒有靠譜的實現。

5.bandwidth-delay product

bandwidth-delay product,中文名帶寬時延乘積指的是一個數據鏈路的能力(throughput)與來回通信延遲(rtt)的乘積。帶寬時延乘積的結果是比特不是位,這個比特值反應出當前網絡管道的最大容量。TCP中有一個窗口大小的概念,會限制發送和接收數據的大小,所以TCP窗口大小的調節是直接受帶寬時延乘積的影響,根據帶寬時延乘積的值去設置套接字的setsockopt方法,設置的option是SO_RCVBUF(接收緩衝區大小)和SO_SNDBUF(發送緩衝區大小)。

通過上面的內容,我們對影響網絡質量的指標有了一定了解,對於不同的產品,影響網絡質量的指標可以理解成一樣的,但對於每個指標的閾值肯定是不一樣的,因爲這包含着業務場景,比如抖音是視頻類網絡傳輸,微信是長連接數據傳輸,百度是文本圖片類數據傳輸。還包括服務端配備,不同產品線的服務集羣能力肯定不一樣,比如返回客戶端的服務端耗時肯定不一樣。所以針對不同的產品弱網指標是基本一致的,但是指標的取值肯定是不一樣的。

如何建立弱網標準

建立弱網標準是一個循序漸進的過程,在一窮二白的時候我們應該如何建立這個標準呢?答案分爲三個階段。

建立弱網標準的步驟:

  1. 第一階段,線下進行測試。獲取一些符合我們預期的閾值,這個時候我們需要藉助一些網絡測試工具,比如蘋果的Network Link Conditioner,Facebook的ATC(Augmented Traffic Control),來獲取到線下不同網絡情況的閾值,一般我們會測試App冷啓動的場景,網絡切換的場景,DNS故障場景,弱網場景(一般都是配置上下行的帶寬,丟包率,延遲,DNS延遲參數,或者更爲簡單的是使用工具默認的一些弱網配置)。

  2. 第二階段,線上進行驗證。通過線下充分測試獲取到的閾值,在線上可以獲取到弱網的比例,在這裏百度App是針對特定場景的,比如Feed刷新,搜索落地頁打開等,就算是在移動時代被大家公認的網絡體驗好的微信,也只是在信令傳輸(收發消息)上做到極致優化,所以針對場景蒐集弱網數據很重要。

  3. 第三階段,線上的反覆試驗。想做到理想的弱網效果,少不了線上反覆的閾值調整,通過調整閾值比較針對場景的網絡請求的成功率、耗時、連接複用率等指標,使我們獲得趨向於針對場景的合理閾值。

聊了這麼多,那麼弱網的探測如何實現呢?

網絡探測的整體架構和實現

網絡探測是弱網檢測的基礎,是否能即時,正確的檢測出網絡質量,是我們首先要解決的問題。我們把網絡探測劃分爲兩部分,主動網絡探測被動網絡採集

1.主動網絡探測

所謂主動探測,就是在觸發了某些條件後,主動的進行網絡探測,並按照一定的條件檢查出是否是弱網狀態。百度App自研了主動探測組件,如下圖所示。

主動網絡探測

策略層

探測策略層通過多種策略的組合,使主動探測的即時性和準確性得以大大提高,我們結合上面的策略層圖來解釋下檢測維度的意義。

我們分別在網絡請求成功和失敗的時候觸發了弱網檢測的邏輯。主要分爲如下三種邏輯。

1)成功時,如何判斷進入弱網狀態?檢查weakhttprtt的閾值,這個值取決於業務的設置(一般這個值會針對特殊場景的請求取95分位或者更大分位的值),大於這個值就會進入弱網檢測,爲了防止頻繁觸發探測加了時間間隔維度,目前定義的是10分鐘。從線下模擬測試來看,只要大於這個閾值,檢測結果必然是弱網狀態。

2)成功時,如何判斷退出弱網狀態?檢查goodhttprtt的閾值,這個值取決於業務的設置(一般這個值會取整體網絡的95分位或者更大分位的值),小於這個值證明要切換回正常網絡狀態,爲了防止頻繁觸發探測加了時間間隔維度,目前定義的是30秒。從線下模擬測試來看,只要小於這個閾值,檢測結果必然是正常狀態。如果大於或者等於這個閾值,也不能證明一定不是正常網絡,所以也需要發起網絡探測,但是由於這是在成功回調裏,頻次會很高,所以我們加上時間間隔的限制30秒,還加入了次數的限制,連續成功次數%次數閾值(4次)等於0。但這看起來還是頻次有點高,所以我們引入了階梯遞增機制,隨着次數的增長,成60秒幾何倍數增長。

3)失敗時,如何判斷進入弱網狀態?首先會判斷連續失敗次數,連續失敗次數/次數閾值(2次)等於1並且連續失敗次數%次數閾值(2次)等於0,相比成功,失敗的次數檢查較爲苛刻,主要還是考慮多次觸發網絡檢測損耗性能

進入弱網狀態後,就會觸發基礎能力層的ping和dns query的探測。

基礎能力層

探測基礎能力層,主要提供弱網檢測的手段,一是dns query,一是ping,百度App使用C++實現了這兩個能力。爲什麼要選用這兩種手段呢?我們在系列二中介紹過,一個網絡請求,分爲DNS-》TLS-》TCP-》數據傳輸 四個階段。想判定網絡連通性主要在DNS和TCP階段,所以dns query和ping就是用來檢測這兩個階段的連通性手段。dns query向百度核心域名mbd.baidu.com發起dns查詢,查詢的DNS服務器爲系統配置的DNS服務器(iOS通過res_ninit函數構建一個__res_state的結構體,Android通過systemproperty獲取net.dns1和net.dns2的值,便可獲取系統配置的DNS服務器),DNS查詢的超時時間爲3s。ping的目標地址爲百度核心域名mbd.baidu.com,ping的次數爲兩次,每次超時時間是默認的1s。

判斷出弱網狀態後,會將結果提供給接口層。

接口層

接口層主要提供主動探測出來的網絡狀態,目前包括GOOD,BAD,UNKNOWN,OFFLINE

1)GOOD:dns查詢成功並且ping也成功,即標記爲GOOD狀態。

2)BAD:ping失敗一次標記爲BAD狀態。

3)UNKNOWN:初始狀態或者識別不出來狀態爲UNKNOWN狀態。

4)OFFLINE:dns server錯誤(沒有獲取到要發送的DNS server地址),網關錯誤(讀取/proc/net/route文件內容失敗),發送dns錯誤(發送dns數據出錯),ping讀寫錯誤(ping的過程中讀寫錯誤),接收dns錯誤(接收dns數據出錯),ping地址錯誤(ping地址是空),dns未知域名錯誤(dns沒有查詢到域名錯誤),初始化icmp錯誤(初始化icmp失敗),dns udp錯誤(創建UDP socket失敗),即標記爲OFFLINE狀態。

2.被動網絡採集

所謂被動採集,就是每一次網絡請求的所有細節都進行記錄,並按照一定的條件將原始信息進行上報,上層再根據條件判斷是否是弱網狀態。百度App基於cronet的NQE(Network Quality Estimator)進行了二次訂製開發。

首先我們講解下需要採集的數據,包括tcprtt、httprtt、throughput三個維度,如下圖所示。

被動採集數據

1)tcprtt,基於posix和windows的socket編程接口來獲取tcprtt。獲取時機在連接完成,讀完成和寫完成。

2)httprtt,基於HTTP協議棧實現,通過計算接收response數據開始和開始發送的時間差,來獲取httprtt。獲取時機在讀首包完成時。

3)throughput,通過上面的計算公式需要獲取bytes和時間,基於posix和windows的socket編程接口來獲取bytes。獲取時機在讀完成時記錄接收的bytes,在寫完成時記錄發送的bytes。時間的獲取在吞吐量管理模塊裏完成,下面會講到。獲取時機在請求完成和請求銷燬時。

如下爲被動網絡採集的整體架構圖。

被動網絡採集

能力層

能力層內容上面我們已經講過,主要採集tcprtt、httprtt、throughput三個維度的數據

策略層

被動採集策略層通過多種策略的組合,降低各種採集數據的上報時機,降低性能的影響

1)套接字管理模塊,首先負責獲取tcprtt的值,如何獲取tcprtt呢?通過getsockopt函數獲取tcp_info結構體裏的tcpi_rtt值。其次由於tcprtt的上報頻次比較頻繁,所以做了1秒的時間間隔上報限制。

2)吞吐量管理模塊,負責吞吐量的計算,上面介紹了計算公式,從網絡活動監控器模塊獲取bytes,但吞吐量的計算單位是bits(位),所以將bytes乘以8。只有GET請求會被列入統計計算,並且至少要累計5個請求後才能開始統計計算。排除精準度的干擾,比如localhost,私有子網上的主機,特定用途子網主機,可參考RFC1918。

3)網絡質量管理模塊,從套接字管理模塊獲取tcprtt,從吞吐量管理模塊獲取吞吐量,並且在HTTP協議棧讀首包完成時獲取httprtt。獲取到這三個值後,需要經過一些策略限制上報的頻次,10秒間隔的限制;網絡類型不能是UNKNOWN(1.3的第三部分會詳細講解);網絡不能頻繁切換;rtt和吞吐量總大小各300個。

接口層

接口層主要提供被動採集出來的網絡狀態,目前包括GOOD,BAD,UNKNOWN,OFFLINE

1)GOOD:3G和廣義的4G,任一條件滿足標記爲GOOD狀態。通過閾值標記3G和廣義的4G,httprtt大於等於273ms,tcprtt大於等於204ms,即標記爲3G狀態。小於這兩個值被標記爲廣義的4G,所謂廣義的4G包含4G、WiFi、以及質量較好的各種接入網絡。

2)BAD:慢2G,2G和httprtt大於1.31秒,任一條件滿足標記爲BAD狀態。通過閾值標記慢2G和2G,httprtt大於等於2.01秒,tcprtt大於等於1.87秒,標記爲慢2G。httprtt大於等於1.42秒,tcprtt大於等於1.28秒,標記爲2G。httprtt大於1.31秒,爲百度App的Feed刷新業務閾值。

注:上面涉及的時間值爲nqe內部的機制,具有普適性。

3)UNKNOWN:非法的httprtt,tcprtt,吞吐量,任一條件滿足標記爲UNKNOWN狀態。何爲非法?值爲-1爲非法,那什麼條件被標記爲-1呢?首先初始化時會被標記爲-1,其次在從來沒有獲取到過httprtt,tcprtt,throughput的值時,會使用本地默認的值做爲判斷標準,這是一種容錯處理。

4)OFFLINE:依賴平臺能力進行判斷,Android平臺依賴ConnectivityManager獲取NetworkInfo,通過NetworkInfo的isConnected獲取是否連接,如果未連接則判斷爲OFFLINE狀態,如果NetworkInfo爲空則判斷爲OFFLINE狀態。

弱網狀態下百度App如何改善用戶體驗

百度App在弱網下的手段

QUIC在百度App弱網下的最佳實踐

QUIC(Quick UDP Internet Connections)是新一代的互聯網傳輸協議,最早源於Google,它的詳盡內容可參考資料【3】,本章我們不做QUIC的科普介紹。

百度App的普通網絡請求在弱網狀態下會切換到QUIC,本章重點講解下百度App針對弱網下開啓QUIC後遇到的問題,一是開啓QUIC一旦遇到問題是否可以回滾?二是在弱網下如何能讓流量儘可能的走QUIC?針對這兩個問題,我們的解決方案是QUIC升降級原理和QUIC預連接

1.1QUIC升降級原理

QUIC升降級原理

如上圖所示,QUIC的升降級依賴於HTTP Alternative Services,HTTP Alternative Services是與HTTP有關的一個協議,它不是爲QUIC專門設計的,在HTTP協議上主要負責新服務的替換,對於HTTP1.1協議,它是通過HTTP響應頭傳輸回來的,所以只能在第二次請求時生效,如下面格式。

Alt-Svc: quic=“alt.example.com:443”, quic=":443"; ma=2592000

如上信息表明切換到quic協議,指定了域名服務和端口,並且指定了生效時間,以秒爲單位。

Alt-Svc: clear

如上信息表明將alter配置清空

在網絡庫內部有一個alter連接和原連接的競爭機制,如果alter信息已經存在,優先發送alter連接,而原連接會延遲發送,延遲時間默認300ms,誰先成功就使用哪個連接,如果alter連接在QUIC握手時失敗,會記錄這個alter信息的失敗次數,並根據失敗的次數,計算出一個過期時間,這個過期時間會隨失敗次數指數增加,最長爲2天。當過期時間到期後,會清除這個alter信息,當這個alter連接在QUIC握手成功後,會清除這個alter信息。

1.2QUIC預連接

所謂QUIC的預連接,就是在進入弱網狀態前提前建立QUIC連接。大家都知道QUIC引以爲傲的0RTT,但第一次建立連接的時候是需要1RTT的,客戶端首先會向服務器發送一個client hello消息,服務器會回覆一個server reject消息,這個消息中包括了server config,有了server config後客戶端就可以直接計算出密鑰,完成0RTT,詳盡內容可參考資料【4】。

通過上面的原理,客戶端拉取server config的成功概率會直接影響QUIC在弱網下的流量,所以我們在App啓動的過程中會做一次QUIC預連接,將server config拉取下來,這樣等進入弱網後alter連接會大概率的競爭過原連接,進而走QUIC協議。

2.複合連接在百度App弱網下的最佳實踐

複合連接的具體原理可以查看《百度App網絡深度優化系列《二》連接優化》裏的具體介紹,百度App目前在弱網下只是針對圖片網絡請求開啓了複合連接,因爲圖片請求不管是HTTPDNS結果還是localDNS的結果都是多個IP,這是滿足複合連接的前提。在弱網下多IP的嘗試會比單IP的結果好些,另外弱網的比例相對較小,複合連接對於服務器的負載也會小些。

百度App網絡整體架構

百度App網絡整體架構

百度App網絡整體架構,以網絡門面爲中間層,隔離上層的最佳實踐和底層的基礎網絡庫

最佳實踐

客戶端網絡庫的一部分工作量是在如何讓最佳實踐做的更好,在音視頻上,不管是iOS的AVPlayer,還是雙端的ijkPlayer,都是使用HTTPDNS組件接管DNS模塊,沒有全部接管網絡模塊。ReactNative的網絡模塊RCTNetworking,圖片庫Android的Fresco和iOS的SDWebImage,WebView組件Android的Chromium和iOS的WKWebView,以及百度App的自有業務,都是通過網絡門面的接口層直接接管。而對於第三方業務考慮到與宿主耦合的關係,直接使用Android的HttpURLConnection和iOS的URLSession系統標準的接口。

網絡門面

網絡門面主要包括,攔截器模塊(提供給業務訂製網絡門面的機制)、併發隊列模塊(提供高中低以及非常高的網絡請求優先級)、網絡探測組件(弱網主動探測能力)、網絡診斷模塊(包括https,ping,dns的校驗)、HTTPDNS組件(《百度App網絡深度優化系列《一》DNS優化》裏詳細講解)、網絡監控模塊(客戶端的打點機制,服務端的例行和突發監控)、HttpURLConnection封裝層、URLSession封裝層。

基礎網絡庫

基礎網絡庫包含兩部分,一部分是基於cronet二次訂製的統一網絡庫,一部分是WebSocket基礎庫(Android的JavaWebSocket,iOS的SocketRocket)。統一網絡庫內部包含連接優化的內容(在《百度App網絡深度優化系列《二》連接優化》裏詳細講解),弱網優化的內容(上面提到的被動採集)。通過AOP的方式將底層協議棧注入進HttpURLConnection(利用URLStreamHandlerFactory)和URLSession(利用URLSessionConfiguration的protocolClasses屬性),兩者都是系統提供的URL Loading機制。

收益

弱網優化的收益我們主要從上面講到的進入弱網狀態後的手段來看,包括開啓QUIC,QUIC預連接,開啓複合連接

1)弱網下開啓QUIC後,網絡連接成功率提升0.01%,平均耗時降低23.5%。

2)弱網下開啓QUIC預連接後,QUIC協議的pv從37萬漲到90萬。

3)弱網下開啓複合連接後,bad狀態下耗時降低2.5%,offline狀態下耗時降低7.7%。

結語

系列一到系列三的內容到今天全部完成,希望能對大家的工作和學習有所幫助,感謝大家的持續關注和鼓勵。生命不息,優化不止,做技術我們是認真的

作者介紹

蔡銳,9年移動端開發經驗,在百度先後主導過訂製 ROM 領域、多屏互動領域、Hybrid跨平臺領域等多個技術領域的開發,目前擔任百度App的客戶端資深工程師,參與基礎技術的研究,專攻動態化、性能和網絡優化方向。

參考資料

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章