網絡優化是客戶端幾大技術方向中公認的一個深度領域,所以百度 App 給大家帶來網絡深度優化系列文章,其中包含系列(一)DNS 優化,系列(二)連接優化,系列(三)弱網優化,希望對大家在網絡方向的學習和實踐有所幫助。
一、前言
在系列(一)裏大家瞭解到網絡優化一般會首選優化DNS,而接下來的HTTP協議成爲優化的重點,一般優化者會選擇協議切換,合併請求,精簡數據包大小等手段來對HTTP協議進行優化,嚴謹的說這都不屬於網絡優化的範疇。
HTTP協議的基礎是連接,所以我們的系列《二》連接優化應運而生,希望對大家在網絡方向的學習和實踐有所幫助。
二、背景
連接優化需要解決兩個核心問題
- 連接建立耗時較長,導致請求的總時長變長,進而影響用戶體驗。
- 在多變的網絡環境下,連接建立的過程可能會失敗,導致成功率下降,進而影響用戶體驗。
百度App承載着億級流量,對於每一個請求都需要追求耗時短,成功率高的體驗。從協議角度出發,如何才能做到這一點呢?首先我們來看下建立連接耗時的原理。
從上圖我們能清晰的看出
- DNS Query需要1個RTT(Round-Trip Time,即往返時間),百度App都是基於HTTPDNS服務的,所以大部分會命中緩存,如果降級走了系統DNS,也會命中緩存,命中不了的由於是基於UDP協議,所以在連接耗時上沒有太大的影響,線上的數據也能說明這點。
- TCP要經歷SYN,SYN/ACK,ACK三次握手的1.5個RTT,不過ACK和ClientHello合併了,所以就是1個RTT。
- TLS(Transport Layer Security,即傳輸層安全性協議)需要經過握手和密鑰交換2個RTT。
綜上所述,DNS,TLS,TCP握手階段用了4個RTT纔到了ApplicationData階段,也就是數據開始傳輸階段。
通過上面的分析可以總結出,如果我們能儘量的將TLS和TCP的RTT減少,將會大大降低連接耗時的時間。
三、連接優化我們都能做什麼
百度App的優化目標分爲兩類,一類是TLS的連接優化,一類是TCP的連接優化。
TLS的連接優化
TLS的連接優化,需要服務端和客戶端都需要支持,共同完成優化手段,包括Session Resumption和False Start。
Session Resumption
Session Resumption中文意思是會話複用,下圖講解了Session Resumption的協議原理。
通過上圖可以看出TLS密鑰協商交換的過程沒有了,但具體是如何實現的呢?包含兩種方式,一種是Sesssion Identifier,一種是Session Ticket。
1)Session Identifier
Session Identifier中文爲會話標識符,更像我們熟知的session的概念。是 TLS 握手中生成的 Session ID。服務端會將Session ID保存起來,客戶端也會存儲Session ID,在後續的ClientHello中帶上它,服務端如果能找到匹配的信息,就可以完成一次快速握手。
2)Session Ticket
Session Identifier存在一些弊端,比如客戶端多次請求如果沒有落在同一臺機器上就無法找到匹配的信息,但Session Ticket可以。Session Ticket更像我們熟知的cookie的概念,Session Ticket用只有服務端知道的安全密鑰加密過的會話信息,保存在客戶端上。客戶端在ClientHello時帶上了Session Ticket,服務器如果能成功解密就可以完成快速握手。
不管是Session Identifier還是Session Ticket都存在時效性問題,不是永久生效,對於這兩種方式大家可以查看參考資料【4】。百度App的網絡協議層對這兩種方式都是支持的,省去了TLS握手過程中證書下載,密鑰協商交換的環節,節省了1個RTT的時間。
False Start
False Start的中文意思是搶跑,下圖講解了False Start的協議原理。
上圖很清晰的說明在TLS第一步握手成功後,客戶端在發送Change Cipher Spec Finished的同時開始數據傳輸,服務端在TLS握手完成時直接返回應用數據。應用數據的發送實際上並未等到握手全部完成,所以稱之爲搶跑。
從結果看省去了1個RTT的時間。False Start有兩個前提條件,一是要通過應用層協議協商ALPN(Application Layer Protocol Negotiation)握手,二是要支持前向安全的加密算法。False Start在未完成握手的情況下就發送了數據,前向安全可以提高安全性,具體協議實現,大家可以查看參考資料【3】。百度App的網絡協議層對False Start是支持的。
這裏說句題外話,其實TCP層有個類似的連接優化手段叫Fast Open,感興趣的同學,可以查看參考資料【5】。
Session Resumption和False Start的區別
兩者對於TLS來說都是節省一個RTT,Session Resumption在第一次握手時還是需要2個RTT,在第二次握手時才能複用減少到1個RTT。False Start是端上的行爲,故每次都會減少到1個RTT。
TCP的連接優化
TCP的連接優化,我們先從連接池說起,首先讓我們來認識下連接池都有哪些類型。
1. 連接池
上圖展示了連接池的不同類型,都是大家耳熟能詳的協議連接池,有低級連接池,包含TCP連接池(管理HTTP請求的連接)和WebSocket連接池(管理WebSocket連接)。
有高級連接池,包括HTTP代理連接池(管理HTTP代理請求的連接),SpdySession連接池(管理SPDY和HTTP/2請求的連接),SOCKS連接池(管理SOCKS和SOCKS5代理的連接),SSL連接池(管理HTTPS請求的連接)。
不同類型的連接池以組合的形式互相複用能力。
1)SSL連接池管理的是SSLSocket,但SSLSocket又依賴於TCP連接池提供的TCPSocket。
2)HTTP代理連接池如果走HTTP協議,那麼就需要TCP連接池提供TCPSocket,如果走HTTPS協議,那麼就需要SSL連接池提供SSLSocket。
3)SpdySession連接池依賴SSL連接池提供SSLSocket,這裏需要說明下,雖然HTTP/2協議沒有強制綁定HTTPS,但是在實際開發中確實都是綁定HTTPS,百度App使用ALPN來協商HTTP/2。
4)SOCKS連接池管理的SOCKSSocket和SOCKS5Socket都需要依賴TCP連接池提供的TCPSocket,雖然SOCKS5支持UDP,但cronet網絡庫暫時沒有實現。
5)WebSocket連接池依賴TCP連接池提供的TCPSocket,聲明下這裏沒有說明WSS(Web Socket Secure)的情況。
TCP連接優化是一個比較複雜的內容,百度App做了針對性場景優化,包括預連接,連接重建,備用連接,複合連接。
2. 預連接
預連接,預先創建好的連接。它解決的場景是在App使用階段可以無耗時的獲取連接。下面用四個問答來解釋預連接。
問題一:預連接是否能解決所有網絡請求的提前連接建立?
答:答案是否定的,預連接需要業務方進行核心業務的評估,針對核心的域名進行預連接的建立。
問題二:預連接既然針對的是特定的域名,那麼是如何配置的呢?
答:採用域名+連接數的方式進行配置,比如https://a.baidu.com|2,表示給a.baidu.com這個域名配置兩條預連接,這裏要說明下,在HTTP/1.x協議下,網絡庫的實現都會對於單域名有最大連接數的限制,不同網絡庫的個數限制不一樣,有5個也有6個,但對於HTTP/2協議,這個連接數就只能是1個。
問題三:預連接是如何建立的?
答:在網絡庫初始化的時候,會根據使用者的配置延遲5s進行預連接的建立,主要是考慮網絡庫在冷啓動下對於啓動性能的影響,爲了保證網絡庫的整體性能,預連接的總個數限制在20個。
問題四:預連接是如何保持的?
答:在網絡庫初始化的時候,除了進行預連接的建立,還會創建一個預連接的定時器,這個定時器會每隔31s,這個值的設定取決於BFE(Baidu Front End,是七層流量的統一接入系統)和BGW(Baidu Gate Way,百度自主研發的四層負載均衡平臺)對超時的最小值設定,根據使用者的配置重新建立連接。
3. 連接重建
連接重建,將連接重新建立。它解決的場景是App網絡狀態發生變化,IP地址變化,導致連接不可用。下面用三個問答來解釋連接重建。
問題一:連接重建是否針對連接池裏的所有連接?
答:答案是肯定的。
問題二:連接重建的過程是什麼樣的?
答:在網絡狀態變化的時候,第一步會清除掉連接池裏的idle socket,何爲idle socket?即空閒socket,對於從未使用過的空閒socket超過60秒清除,對於使用過的空閒socket超過90秒清除。第二步重建連接需要等待200ms,目的是等待DNS先重建完成。
問題三:連接重建對於性能有影響嗎?
答:出於性能考慮,連接重建的連接個數限制是100個。
4. 備用連接
備用連接,預備的連接。它解決的場景是正常發送一個請求當group內無連接可用的時候(何爲group?group是管理socket的最小單元,內部包含活躍socket,空閒socket,連接任務,等待請求)。下面用三個問答來解釋備用連接。
問題一:備用連接是否針對所有請求?
答:答案是肯定的。
問題二:備用連接的過程是什麼樣的?
答:當有請求來臨時,連接池內無連接可用,會啓動一個定時器開啓備用連接,定時器的間隔時間是250ms,與主連接進行競爭,如果主連接因爲網絡抖動或者網絡狀態不好,導致連接失敗,那麼備用連接就直接發送請求。如果主連接成功,那麼備用連接就被取消掉。
問題三:備用連接的目的是什麼?
答:在連接池無連接的情況下,務必是要創建連接的,在主連接之外加一個備用連接,會大大提升創建連接的成功率,從而提升用戶體驗。
5. 複合連接:
複合連接,即多條連接。它解決的場景是爲了多個IP地址的連接選取問題。下面用三個問答來解釋複合連接。
問題一:複合連接是否針對所有請求?
答:答案是肯定的。複合連接可以全局開關,百度App現階段暫時沒有開啓複合連接。
問題二:複合連接的過程是什麼樣的?
答:衆所周知域名DNS查詢一般情況下會返回多個IP,我們以域名查詢返回兩個IP爲例
1)如果結果中存在IPv6的地址,那麼會優先選用IPv6的地址,這個規則follow HappyEyeBall機制(可參考系列一對於HappyEyeBall的介紹)。
2) 接下來這兩個IP會按照順序嘗試建立連接,如果第一個IP返回失敗,將立即開始連接第二個IP。
3)如果第一個IP率先成功返回,那麼第二個IP將被加入連接嘗試列表並停止所有嘗試連接。
4)如果第一個IP失敗,會立刻開始第二個IP的連接。
5)如果第一個IP處於pending狀態,那麼會啓動一個定時器,默認延遲2s會發起第二個IP的連接,如果是多個IP將會遞歸連接,需要特別說明下,不同的網絡制式延遲時間會不一樣,這樣體驗也會更好。
問題三:複合連接的目的是什麼?
答:複合連接的好處是提供最優的IP選取機制,但也會帶來服務端的高負載,所以使用的時候需要進行綜合評估。
四、連接優化的最佳實踐
百度App目前客戶端網絡架構由於歷史原因還未統一,不過我們正朝着這個目標努力。
我們的中心思想是以系統網絡庫的API調用接口爲中心,上層建立網絡門面,供外部便捷調用,底層通過系統機制以AOP的方式將cronet(chromium的net模塊)注入進系統網路庫,達到雙端網絡架構統一,能力複用。
下面着重介紹下連接優化在Android和iOS網絡架構中的位置及實踐。
- 連接優化在Android網絡架構的位置及實踐
百度App的Android網絡流量目前都在okhttp之上,上層進行了網絡門面的封裝,封裝內部的實現細節和對外友好的API,目前我們正在進行重構,默認採用Android標準的網絡接口HttpURLConnection,它的底層由系統提供的okhttp的實現。
訂製方面利用URL Stream Protocol機制將HttpURLConnection底層網絡協議棧接管爲cronet,供各個業務和基礎模塊使用,連接優化的所有內容在cronet網絡庫內部實現。
2. 連接優化在iOS網絡架構的位置及實踐
百度App的iOS網絡流量目前都在cronet之上,上層我們使用iOS的URL Loading System機制將cronet stack注入進URLSession裏,這樣我們就可以直接使用URLSession的API進行網絡的操作而且更易於系統維護,在上層封裝了網絡門面,供各個業務和基礎模塊使用。
在cronet內部實現了預連接(主要針對百度App的幾個核心域名進行預連和保活),連接重建(針對所有請求),備用連接(針對所有請求),複合連接(iOS上暫時沒有開啓),Session Resumption(針對所有請求),False Start(針對所有請求)。
五、收益
連接優化的收益主要體現在網絡時延和網絡成功率上,這兩點收益需要結合業務來說,以百度App Feed刷新這個典型業務場景爲例。
Feed刷新文本請求網絡時延降低16%,Feed刷新圖片請求網絡時延降低12%,可謂收益相當明顯。
成功率方面,Feed刷新文本請求成功率提升0.29%,Feed刷新圖片請求成功率提升0.23%,也是非常不錯的收益。
六、結語
連接優化是個持續性的話題,沒有最優只有更優。上面介紹的百度App的一些經驗和做法並不見得完美,但我們會繼續深入的優化下去,持續提升百度App的網絡性能。
以上優化由百度App團隊,內核團隊,OP團隊共建完成。最後感謝大家的辛苦閱讀,希望對你有所幫助,後面會繼續推出-百度App網絡深度優化系列《三》弱網優化,敬請期待。
七、參考資料
- https://chromium.googlesource.com/chromium/src/+/HEAD/docs/android_build_instructions.md
- https://chromium.googlesource.com/chromium/src/+/HEAD/docs/ios/build_instructions.md
- https://tools.ietf.org/html/rfc7918 False Start
- https://tools.ietf.org/html/rfc5077 Session Resumption
- https://tools.ietf.org/html/rfc7413 TCP Fast Open
作者簡介
蔡銳,9 年移動客戶端開發經驗,在百度先後主導過訂製 ROM 領域、多屏互動領域、Hybrid 跨平臺領域等多個技術領域的開發,目前擔任百度 App 的客戶端資深工程師,參與基礎技術的研究,專攻動態化和網絡優化方向。