16 | 網絡優化(中):復雜多變的移動網絡該如何優化?


在 PC 互聯網時代,網絡優化已經是一項非常復雜的工作。對於移動網絡來說,弱網絡、網絡切換、網絡劫持這些問題更加突出,網絡優化這項工作也變得更加艱巨。

那作為一名移動開發者,面對復雜多變的移動網絡我們該如何去優化呢?可能也有人會說,我只要用好 AFNetworking/OkHttp 這些成熟網絡庫就可以了,並不需要額外去做什么優化。那你確定你真的能用好這些網絡庫嗎?它們內部是怎樣實現的、有哪些差異點、哪個網絡庫更好呢?

雖然我們可能只是客戶端 App 開發人員,但在關於網絡優化還是可以做很多事情的,很多大型的應用也做了很多的實踐。今天我們一起來看一下,如何讓我們的應用在各種的網絡條件下都能“快人一步”。

移動端優化

回想上一期我給出的網絡架構圖,一個數據包從手機出發要經過無線網絡、核心網絡以及外部網絡(互聯網),才能到達我們的服務器。那整個網絡請求的速度會跟哪些因素有關呢?

network_2_1

從上面這張圖上看,客戶端網絡庫實現、服務器性能以及網絡鏈路的質量都是影響網絡請求速度的因素。下面我們先從客戶端的網絡庫說過,看看應該如何進行網絡優化。

1. 何為網絡優化

在講怎么去優化網絡之前,我想先明確一下所謂的網絡優化,究竟指的是什么?在我看來,核心內容有以下三個:

  • 速度。在網絡正常或者良好的時候,怎樣更好地利用帶寬,進一步提升網絡請求速度。
  • 弱網絡。移動端網絡復雜多變,在出現網絡連接不穩定的時候,怎樣最大程度保證網絡的連通性。
  • 安全。網絡安全不容忽視,怎樣有效防止被第三方劫持、竊聽甚至篡改。

除了這三個問題,我們可能還會關心網絡請求造成的耗電、流量問題,這兩塊內容我們在后面會統一地講,今天就不再展開。

那對於速度、弱網絡以及安全的優化,又該從哪些方面入手呢?首先你需要先搞清楚一個網絡請求的整個過程。

network_2_2

從圖上看到,整個網絡請求主要分為幾個步驟,而整個請求的耗時可以細分到每一個步驟里面。

  • DNS 解析。通過 DNS 服務器,拿到對應域名的 IP 地址。在這個步驟,我們比較關注 DNS 解析耗時情況、運營商 LocalDNS 的劫持、DNS 調度這些問題。
  • 創建連接。跟服務器建立連接,這里包括 TCP 三次握手、TLS 密鑰協商等工作。多個 IP/ 端口該如何選擇、是否要使用 HTTPS、能否可以減少甚至省下創建連接的時間,這些問題都是我們優化的關鍵。
  • 發送 / 接收數據。在成功建立連接之后,就可以愉快地跟服務器交互,進行組裝數據、發送數據、接收數據、解析數據。我們關注的是,如何根據網絡狀況將帶寬利用好,怎么樣快速地偵測到網絡延時,在弱網絡下如何調整包大小等問題。
  • 關閉連接。連接的關閉看起來非常簡單,其實這里的水也很深。這里主要關注主動關閉和被動關閉兩種情況,一般我們都希望客戶端可以主動關閉連接。

所謂的網絡優化,就是圍繞速度、弱網絡、安全這三個核心內容,減少每一個步驟的耗時,打造快速、穩定且安全的高質量網絡。

2. 何為網絡庫

在實際的開發工作中,我們很少會像《UNIX 網絡編程》那樣直接去操作底層的網絡接口,一般都會使用網絡庫。Square 出品的OkHttp是目前最流行的 Android 網絡庫,它還被 Google 加入到 Android 系統內部,為廣大開發者提供網絡服務。

那網絡庫究竟承擔着一個什么樣的角色呢?在我看來,它屏蔽了下層復雜的網絡接口,讓我們可以更高效地使用網絡請求。

network_2_3

如上圖所示,一個網絡庫的核心作用主要有以下三點:

  • 統一編程接口。無論是同步還是異步請求,接口都非常簡單易用。同時我們可以統一做策略管理,統一進行流解析(JSON、XML、Protocol Buffers)等。
  • 全局網絡控制。在網絡庫內部我們可以做統一的網絡調度、流量監控以及容災管理等工作。
  • 高性能。既然我們把所有的網絡請求都交給了網絡庫,那網絡庫是否實現高性能就至關重要。既然要實現高性能,那我會非常關注速度,CPU、內存、I/O 的使用,以及失敗率、崩潰率、協議的兼容性等方面。

不同的網絡庫實現差別很大,比較關鍵有這幾個模塊:

network_2_4

那網絡庫實現到底哪家強?接下來我們一起來對比 OkHttp、Chromium 的Cronet以及微信Mars這三個網絡庫的內部實現。

高質量網絡庫

據我了解業內的蘑菇街、頭條、UC 瀏覽器都在 Chromium 網絡庫上做了二次開發,而微信 Mars 在弱網絡方面做了大量優化,拼多多、虎牙、鏈家、美麗說這些應用都在使用 Mars。

下面我們一起來對比一下各個網絡庫的核心實現。對於參與網絡庫相關工作來說,我的經驗還算是比較豐富的。在微信的時候曾經參與過 Mars 的開發,目前也在基於 Chromium 網絡庫做二次開發。

network_2_5

為什么我從來沒使用過 OkHttp?主要因為它並不支持跨平台,對於大型應用來說跨平台是非常重要的。我們不希望所有的優化 Android 和 iOS 都要各自去實現一套,不僅浪費人力而且還容易出問題。

對於 Mars 來說,它是一個跨平台的 Socket 層解決方案,並不支持完整的 HTTP 協議,所以 Mars 從嚴格意義上來講並不是一個完整的網絡庫。但是它在弱網絡和連接上做了大量的優化,並且支持長連接。關於 Mars 的網絡多優化的更多細節,你可以參考Wiki右側的文章列表。

network_2_5

Chromium 網絡庫作為標准的網絡庫,基本上可以說是找不到太大的缺點。而且我們可以享受 Google 后續網絡優化的成果,類似 TLS 1.3、QUIC 支持等。

但是它針對弱網絡場景沒有做太多定制的優化,也不支持長連接。事實上目前我在 Chromium 網絡庫的二次開發主要工作也是補齊弱網絡優化與長連接這兩個短板。

大網絡平台

對於大公司來說,我們不能只局限在客戶端網絡庫的雙端統一上。網絡優化不僅僅是客戶端的事情,所以我們有了統一的網絡中台,它負責提供前后台一整套的網絡解決方案。

阿里的ACCS、螞蟻的mPaaS、攜程的網絡服務都是公司級的網絡中台服務,這樣所有的網絡優化可以讓整個集團的所有接入應用受益。

下圖是 mPaaS 的網絡架構圖,所有網絡請求都會先經過統一的接入層,再轉發到業務服務器。這樣我們可以在業務服務器無感知的情況下,在接入層做各種各樣的網絡優化。

network_2_7

1. HTTPDNS

DNS 的解析是我們網絡請求的第一項工作,默認我們使用運營商的 LocalDNS 服務。這塊耗時在 3G 網絡下可能是 200~300ms,4G 網絡也需要 100ms。

解析慢並不是默認 LocalDNS 最大的“原罪”,它還存在一些其他問題:

  • 穩定性。UDP 協議,無狀態,容易域名劫持(難復現、難定位、難解決),每天至少幾百萬個域名被劫持,一年至少十次大規模事件。
  • 准確性。LocalDNS 調度經常出現不准確,比如北京的用戶調度到廣東 IP,移動的運營商調度到電信的 IP,跨運營商調度會導致訪問慢,甚至訪問不了。
  • 及時性。運營商可能會修改 DNS 的 TTL,導致 DNS 修改生效延遲。不同運營商的服務實現不一致,我們也很難保證 DNS 解析的耗時。

為了解決這些問題,就有了 HTTPDNS。簡單來說自己做域名解析的工作,通過 HTTP 請求后台去拿到域名對應的 IP 地址,直接解決上述所有問題。

微信有自己部署的 NEWDNS,阿里雲和騰訊雲也有提供自己的 HTTPDNS 服務。對於大網絡平台來說,我們會有統一的 HTTPDNS 服務,並將它和運維系統打通。在傳統的 DNS 基礎上,還會增加精准的流量調度、網絡撥測 / 灰度、網絡容災等功能。

network_2_8

關於 HTTPDNS 的更多知識,你可以參考百度的《DNS 優化》。對客戶端來說,我們可以通過預請求的方法,提前拿到一批域名的 IP,不過這里需要注意 IPv4 與 IPv6 協議棧的選擇問題。

2. 連接復用

在 DNS 解析之后,我們來到了創建連接這個環節。創建連接要經過 TCP 三次握手、TLS 密鑰協商,連接建立的代價是非常大的。這里我們主要的優化思路是復用連接,這樣不用每次請求都重新建立連接。

在前面我就講過連接管理,網絡庫並不會立刻把連接釋放,而是放到連接池中。這時如果有另一個請求的域名和端口是一樣的,就直接拿出連接池中的連接進行發送和接收數據,少了建立連接的耗時。

這里我們利用 HTTP 協議里的 keep-alive,而 HTTP/2.0 的多路復用則可以進一步的提升連接復用率。它復用的這條連接支持同時處理多條請求,所有請求都可以並發在這條連接上進行。

network_2_9

雖然 H2 十分強大,不過這里還有兩個問題需要解決。一個是同一條 H2 連接只支持同一個域名,一個是后端支持 HTTP/2.0 需要額外的改造。這個時候我們只需要在統一接入層做改造,接入層將數據轉換到 HTTP/1.1 再轉發到對應域名的服務器。

network_2_10

這樣所有的服務都不用做任何改造就可以享受 HTTP/2.0 的所有優化,不過這里需要注意的是 H2 的多路復用在本質上依然是同一條 TCP 連接,如果所有的域名的請求都集中在某一條連接中,在網絡擁塞的時候容易出現 TCP 隊首阻塞問題。

對於客戶端網絡庫來說,無論 OkHttp 還是 Chromium 網絡庫對於 HTTP/2.0 的連接,同一個域名只會保留一條連接。對於一些第三方請求,特別是文件下載以及視頻播放這些場景可能會遇到對方服務器單連接限速的問題。在這種情況下我們可以通過修改網絡庫實現,也可以簡單的通過禁用 HTTP/2.0 協議解決。

3. 壓縮與加密

壓縮

講完連接,我們再來看看發送和接收的優化。我第一時間想到的還是減少傳輸的數據量,也就是我們常說的數據壓縮。首先對於 HTTP 請求來說,數據主要包括三個部分:

  • 請求 URL
  • 請求 header
  • 請求 body

對於 header 來說,如果使用 HTTP/2.0 連接本身的頭部壓縮技術,因此需要壓縮的主要是請求 URL 和請求 body。

對於請求 URL 來說,一般會帶很多的公共參數,這些參數大部分都是不變的。這樣不變的參數客戶端只需要上傳一次即可,其他請求我們可以在接入層中進行參數擴展。

對於請求 body 來說,一方面是數據通信協議的選擇,在網絡傳輸中目前最流行的兩種數據序列化方式是 JSON 和 Protocol Buffers。正如我之前所說的一樣,Protocol Buffers 使用起來更加復雜一些,但在數據壓縮率、序列化與反序列化速度上面都有很大的優勢。

另外一方面是壓縮算法的選擇,通用的壓縮算法主要是如 gzip,Google 的Brotli或者 Facebook 的Z-standard都是壓縮率更高的算法。其中如果 Z-standard 通過業務數據樣本訓練出適合的字典,是目前壓縮率表現最好的算法。但是各個業務維護字典的成本比較大,這個時候我們的大網絡平台的統一接入層又可以大顯神威了。

network_2_11

例如我們可以抽樣 1% 的請求數據用來訓練字典,字典的下發與更新都由統一接入層負責,業務並不需要關心。

當然針對特定數據我們還有其他的壓縮方法,例如針對圖片我們可以使用 webp、hevc、SharpP等壓縮率更高的格式。另外一方面,基於 AI 的圖片超清化也是一大神器,QQ 空間通過這個技術節約了大量的帶寬成本。

安全

數據安全也是網絡重中之重的一個環節,在大網絡平台中我們都是基於 HTTPS 的 HTTP/2 通道,已經有了 TLS 加密。如果大家不熟悉 TLS 的基礎知識,可以參考微信后台一個小伙伴寫的《TLS 協議分析》

但是 HTTPS 帶來的代價也是不小的,它需要 2-RTT 的協商成本,在弱網絡下時延不可接受。同時后台服務解密的成本也十分高昂,在大型企業中需要單獨的集群來做這個事情。

HTTPS 的優化有下面幾個思路:

  • 連接復用率。通過多個域名共用同一個 HTTP/2 連接、長連接等方式提升連接復用率。
  • 減少握手次數。TLS 1.3可以實現 0-RTT 協商,事實上在 TLS 1.3 release 之前,微信的mmtls、Facebook 的fizz、阿里的 SlightSSL 都已在企業內部大規模部署。
  • 性能提升。使用 ecc 證書代替 RSA,服務端簽名的性能可以提升 4~10 倍,但是客戶端校驗性能降低了約 20 倍,從 10 微秒級降低到 100 微秒級。另外一方面可以通過 Session Ticket 會話復用,節省一個 RTT 耗時。

使用 HTTPS 之后,整個通道是不是就一定高枕無憂呢?如果客戶端設置了代理,TLS 加密的數據可以被解開並可能被利用 。這個時候我們可以在客戶端將“證書鎖定”(Certificate Pinning),為了老版本兼容和證書替換的靈活性,建議鎖定根證書。

我們也可以對傳輸內容做二次加密,這塊在統一接入層實現,業務服務器也同樣無需關心這個流程。需要注意的是二次加密會增加客戶端與服務器的處理耗時,我們需要在安全性與性能之間做一個取舍。

network_2_12

4. 其他優化

關於網絡優化的手段還有很多,一些方案可能是需要用錢堆出來的,比如部署跨國的專線、加速點,多 IDC 就近接入等。

除此之外,使用CDN 服務P2P 技術也是比較常用的手段,特別在直播這類場景。總的來說,網絡優化我們需要綜合用戶體驗、帶寬成本以及硬件成本等多個因素來考慮。

下面為你獻上一張高質量網絡的全景大圖。

network_2_13

QUIC 與 IPv6

今天已經講得很多了,可能還有小伙伴比較關心最近一些比較前沿的技術,我簡單講一下 QUIC 和 IPv6。

1. QUIC

QUIC 協議由 Google 在 2013 年實現,在 2018 年基於 QUIC 協議的 HTTP 更被確認為HTTP/3。在連接復用中我說過 HTTP/2 + TCP 會存在隊首阻塞的問題,基於 UDP 的 QUIC 才是終極解決方案。

如下圖所示,你可以把 QUIC 簡單理解為 HTTP/2.0 + TLS 1.3 + UDP。

network_2_14

事實上,它還有着其他的很多優勢:

  • 靈活控制擁塞協議。如果想對 TCP 內部的擁塞控制算法等模塊進行優化和升級,整體周期是相對較長的。對於 UDP 來說,我們不需要操作系統支持,隨時可改,例如可以直接使用 Google 的BBR 算法
  • “真”連接復用。不僅解決了隊首阻塞的問題,在客戶端網絡切換的時候也不需要重連,用戶使用 App 的體驗會更加流暢。

既然 QUIC 那么好,為什么我們在生產環境沒有全部切換成 QUIC 呢?那是因為有很多坑還沒有踩完,目前發現的主要問題還有:

  • 創建連接成功率。主要是 UDP 的穿透性問題,NAT 局域網路由、交換機、防火牆等會禁止 UDP 443 通行,目前 QUIC 在國內建連的成功率大約在 95% 左右。
  • 運營商支持。運營商針對 UDP 通道支持不足,表現也不穩定。例如 QoS 限速丟包,有些小的運營商甚至還直接不支持 UDP 包。

盡管有這樣那樣的問題,但是 QUIC 一定是未來。當然,通過大網絡平台的統一接入層,我們業務基本無需做什么修改。目前據我了解,騰訊微博阿里都在內部逐步加大 QUIC 的流量,具體細節可以參考我給出的鏈接。

2. IPv6

運維人員都會深深的感覺到 IP 資源的珍貴,而致力於解決這個問題的 IPv6 卻在中國一直非常沉寂。根據《2017 年 IPV6 支持度報告》,在中國只有 0.38% 的用戶使用 v6。

network_2_15

IPv6 不僅針對 IoT 技術,對萬物互聯的時代有着非常大的意義。而且它對網絡性能也有正向的作用,在印度經過我們測試,使用 IPv6 網絡相比 IPv4 連接耗時可以降低 10%~20%。推行 IPv6 后,無窮無盡的 IP 地址意味着可以告別各種 NAT,P2P、QUIC 的連接也不再是問題。

在過去的一年,無論是阿里雲還是騰訊雲都做了大量 IPv6 的工作。當然主要也是接入層的改造,盡量不需要業務服務做太多修改。

總結

移動技術發展到今天,跨終端和跨技術棧的聯合優化會變得越來越普遍。有的時候我們需要跳出客戶端開發的視角,從更高的維度去思考整個大網絡平台。當然網絡優化的水還是非常深的,有時候我們需要對協議層也有比較深入的研究,也要經常關注國外的一些新的研究成果。

2018 年隨着工信部發布《推進互聯網協議第六版(IPv6)規模部署行動計划》的通知,所有的雲提供商需要在 2020 年完成 IPv6 的支持。QUIC 在 2018 年被定為 HTTP/3 草案,同時 3GPP 也將 QUIC 列入 5G 核心網協議第二階段標准(3GPP Release 16)。

隨着 5G、QUIC 與 IPv6 未來在中國的普及,網絡優化永不止步,它們將推動我們繼續努力去做更多嘗試,讓用戶可以有更好的網絡體驗。

課后作業

網絡優化是一個很大的話題,在課后你還需要進一步擴展學習。除了今天文章里給出的鏈接,這里還提供一些參考資料給你:

 

https://blog.yorek.xyz/android/paid/master/network_2/


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM