一般開發一個 APP,會直接調用系統提供的網絡請求接口去服務端請求資料,再針對傳回的資料進行一些處理,或者使用AFNetworking/OKHttp這樣的網絡庫,管理好請求線程和隊列,再自動做一些資料解析,就結束了。
但對于一些大型 APP,還會想針對網絡的一些問題進行進一步優化,包括:
- 速度:網絡請求的速度怎樣能進一步提升?
- 弱網:移動端網絡環境随時變化,經常出現網絡連接配接很不穩定可用性差的情況,怎樣在這種情況下最大限度最快地成功請求?
- 安全:怎樣防止被第三方竊聽/篡改或冒充,防止營運商劫持,同時又不影響性能?
對基于浏覽器的前端開發來說,網絡這塊能做的事情很少,但對于用戶端 APP 來說,整個網絡請求過程是自由控制的,可以做很多事情,很多大型 APP 都針對這三個問題做了很多網絡層的優化,一些新的網絡層協定像 HTTP2 / QUIC 也是在這些方面進行了不少優化,在這裡邊學習邊整理,大緻列舉一下常見的做法。
速度
正常一條網絡請求需要經過的流程是這樣:
- DNS 解析,請求DNS伺服器,擷取域名對應的 IP 位址。
- 與服務端建立連接配接,包括 tcp 三次握手,安全協定同步流程。
- 連接配接建立完成,發送和接收資料,解碼資料。
這裡有明顯的三個優化點:
- 直接使用 IP 位址,去除 DNS 解析步驟。
- 不要每次請求都重建立立連接配接,複用連接配接或一直使用同一條連接配接(長連接配接)。
- 壓縮資料,減小傳輸的資料大小。
逐條來看能做什麼。
1.DNS
DNS 完整的解析流程很長,會先從本地系統緩存取,若沒有就到最近的 DNS 伺服器取,若沒有再到主域名伺服器取,每一層都有緩存,但為了域名解析的實時性,每一層緩存都有過期時間,這種 DNS 解析機制有幾個缺點:
- 緩存時間設定得長,域名更新不及時,設定得短,大量 DNS 解析請求影響請求速度。
- 域名劫持,容易被中間人攻擊,或被營運商劫持,把域名解析到第三方 IP 位址,據統計劫持率會達到7%。
- DNS 解析過程不受控制,無法保證解析到最快的IP
- 一次請求隻能解析一個域名。
為了解決這些問題,就有了 HTTPDNS,原理很簡單,就是自己做域名解析的工作,通過 HTTP 請求背景去拿到域名對應的 IP 位址,直接解決上述所有問題:
- 域名解析與請求分離,所有請求都直接用IP位址,無需 DNS 解析,APP 定時請求 HTTPDNS 伺服器更新IP位址即可。
- 通過簽名等方式,保證 HTTPDNS 請求的安全,避免被劫持。
- DNS 解析由自己控制,可以確定根據使用者所在地傳回就近的 IP 位址,或根據用戶端測速結果使用速度最快的 IP。
- 一次請求可以解析多個域名。
其餘細節就不多說了,HTTPDNS 優點這麼多,幾乎成為中大型 APP 的标配。至此解決了第一個問題 — DNS 解析耗時的問題,順便把一部分安全問題 — DNS 劫持也解決了。
2.連接配接
第二個問題,連接配接建立耗時的問題,這裡主要的優化思路是複用連接配接,不用每次請求都重建立立連接配接,如何更有效率地複用連接配接,可以說是網絡請求速度優化裡最主要的點了,并且這裡的優化仍在演進過程中,值得了解下。
keep-alive
HTTP 協定裡有個 keep-alive,HTTP1.1預設開啟,一定程度上緩解了每次請求都要進行TCP三向交握建立連接配接的耗時。原理是請求完成後不立即釋放連接配接,而是放入連接配接池中,若這時有另一個請求要發出,請求的域名和端口是一樣的,就直接拿出連接配接池中的連接配接進行發送和接收資料,少了建立連接配接的耗時。
實際上現在無論是用戶端還是浏覽器都預設開啟了keep-alive,對同個域名不會再有每發一個請求就進行一次建連的情況,純短連接配接已經不存在了。但有個問題,就是這個 keep-alive 的連接配接一次隻能發送接收一個請求,在上一個請求處理完成之前,無法接受新的請求。若同時發起多個請求,就有兩種情況:
- 若串行發送請求,可以一直複用一個連接配接,但速度很慢,每個請求都要等待上個請求完成再進行發送。
- 若并行發送這些請求,那麼首次每個請求都要進行tcp三次握手建立新的連接配接,雖然第二次可以複用連接配接池裡這堆連接配接,但若連接配接池裡保持的連接配接過多,對服務端資源産生較大浪費,若限制了保持的連接配接數,并行請求裡超出的連接配接仍每次要建連。
對這個問題,新一代協定 HTTP2 提出了多路複用去解決。
多路複用
HTTP2 的多路複用機制一樣是複用連接配接,但它複用的這條連接配接支援同時處理多條請求,所有請求都可以并發在這條連接配接上進行,也就解決了上面說的并發請求需要建立多條連接配接帶來的問題,網絡上有張圖可以較形象地表現這個過程:


HTTP1.1的協定裡,在一個連接配接裡傳送資料都是串行順序傳送的,必須等上一個請求全部處理完後,下一個請求才能進行處理,導緻這些請求期間這條連接配接并不是滿帶寬傳輸的,即使是HTTP1.1的pipelining可以同時發送多個request,但response仍是按請求的順序串行傳回,隻要其中一個請求的response稍微大一點或發生錯誤,就會阻塞住後面的請求。
HTTP2 這裡的多路複用協定解決了這些問題,它把在連接配接裡傳輸的資料都封裝成一個個stream,每個stream都有辨別,stream的發送和接收可以是亂序的,不依賴順序,也就不會有阻塞的問題,接收端可以根據stream的辨別去區分屬于哪個請求,再進行資料拼接,得到最終資料。
解釋下多路複用這個詞,多路可以認為是多個連接配接,多個操作,複用就是字面上的意思,複用一條連接配接或一個線程。HTTP2這裡是連接配接的多路複用,網絡相關的還有一個I/O的多路複用(select/epoll),指通過事件驅動的方式讓多個網絡請求傳回的資料在同一條線程裡完成讀寫。
用戶端來說,iOS9 以上 NSURLSession 原生支援 HTTP2,隻要服務端也支援就可以直接使用,Android 的 okhttp3 以上也支援了 HTTP2,國内一些大型 APP 會自建網絡層,支援 HTTP2 的多路複用,避免系統的限制以及根據自身業務需要增加一些特性,例如微信的開源網絡庫 mars,做到一條長連接配接處理微信上的大部分請求,多路複用的特性上基本跟 HTTP2 一緻。
TCP隊頭阻塞
HTTP2 的多路複用看起來是完美的解決方案,但還有個問題,就是隊頭阻塞,這是受限于 TCP 協定,TCP 協定為了保證資料的可靠性,若傳輸過程中一個 TCP 包丢失,會等待這個包重傳後,才會處理後續的包。HTTP2的多路複用讓所有請求都在同一條連接配接進行,中間有一個包丢失,就會阻塞等待重傳,所有請求也就被阻塞了。
對于這個問題不改變 TCP 協定就無法優化,但 TCP 協定依賴作業系統實作以及部分硬體的定制,改進緩慢,于是 GOOGLE 提出 QUIC 協定,相當于在 UDP 協定之上再定義一套可靠傳輸協定,解決 TCP 的一些缺陷,包括隊頭阻塞。具體解決原理網上資料較多,可以看看。
QUIC 處于起步階段,少有用戶端接入,QUIC 協定相對于 HTTP2 最大的優勢是對TCP隊頭阻塞的解決,其他的像安全握手 0RTT / 證書壓縮等優化 TLS1.3 已跟進,可以用于 HTTP2,并不是獨有特性。TCP 隊頭阻塞在 HTTP2 上對性能的影響有多大,在速度上 QUIC 能帶來多大提升待研究。
3.資料
第三個問題,傳輸資料大小的問題。資料對請求速度的影響分兩方面,一是壓縮率,二是解壓序列化反序列化的速度。目前最流行的兩種資料格式是 json 和 protobuf,json 是字元串,protobuf 是二進制,即使用各種壓縮算法壓縮後,protobuf 仍會比 json 小,資料量上 protobuf 有優勢,序列化速度 protobuf 也有一些優勢,這兩者的對比就不細說了。
壓縮算法多種多樣,也在不斷演進,最新出的 Brotli 和Z-standard實作了更高的壓縮率,Z-standard 可以根據業務資料樣本訓練出适合的字典,進一步提高壓縮率,目前壓縮率表現最好的算法。
除了傳輸的 body 資料,每個請求 HTTP 協定頭的資料也是不可忽視,HTTP2 裡對 HTTP 協定頭也進行了壓縮,HTTP 頭大多是重複資料,固定的字段如 method 可以用靜态字典,不固定但多個請求重複的字段例如 cookie 用動态字典,可以達到非常高的壓縮率,這裡有詳細介紹。
通過 HTTPDNS,連接配接多路複用,更好的資料壓縮算法,可以把網絡請求的速度優化到較不錯的程度了,接下來再看看弱網和安全上可以做的事情。
弱網
手機無線網絡環境不穩定,針對弱網的優化,微信有較多實踐和分享,包括:
-
提升連接配接成功率
複合連接配接,建立連接配接時,階梯式并發連接配接,其中一條連通後其他連接配接都關閉。這個方案結合串行和并發的優勢,提高弱網下的連接配接成功率,同時又不會增加伺服器資源消耗:
移動 APP 網絡優化概述 -
制定最合适的逾時時間
對總讀寫逾時(從請求到響應的逾時)、首包逾時、包包逾時(兩個資料段之間的逾時)時間制定不同的計算方案,加快對逾時的判斷,減少等待時間,盡早重試。這裡的逾時時間還可以根據網絡狀态動态設定。
-
調優TCP參數,使用TCP優化算法。
對服務端的TCP協定參數進行調優,以及開啟各種優化算法,使得适合業務特性和移動端網絡環境,包括RTO初始值,混合慢啟動,TLP,F-RTO等。
針對弱網的這些細緻優化未成為标準,系統網絡庫沒有内置,不過前兩個用戶端優化微信的開源網絡庫 mars 有實作,若有需要可以使用。
安全
标準協定 TLS 保證了網絡傳輸的安全,前身是 SSL,不斷在演進,目前最新是 TLS1.3。常見的 HTTPS 就是 HTTP 協定加上 TLS 安全協定。
安全協定概括性地說解決兩個問題:1.保證安全 2. 降低加密成本
在保證安全上:
- 使用加密算法組合對傳輸資料加密,避免被竊聽和篡改。
- 認證對方身份,避免被第三方冒充。
- 加密算法保持靈活可更新,防止定死算法被破解後無法更換,禁用已被破解的算法。
降低加密成本上:
- 用對稱加密算法加密傳輸資料,解決非對稱加密算法的性能低以及長度限制問題。
- 緩存安全協定握手後的密鑰等資料,加快第二次建連的速度。
- 加快握手過程:2RTT-> 0RTT。加快握手的思路,就是原本用戶端和服務端需要協商使用什麼算法後才能加密發送資料,變成通過内置的公鑰和預設的算法,在握手的同時就把資料發出去,也就是不需要等待握手就開始發送資料,達到0RTT。
這些點涉及的細節非常多,對 TLS 的介紹有一篇雄文,說得很詳細,在此推薦。
目前基本主流都支援 TLS1.2,iOS 網絡庫預設使用 TLS1.2,Android4.4 以上支援 1.2。TLS1.3 iOS 還處于測試階段,Android 未查到消息。對于普通 APP,隻要正确配置證書,TLS1.2 已經能保證傳輸安全,隻是在建連速度上會有所損耗,有一些大型 APP 像微信就自行實作了 TLS1.3 的部分協定,早一步全平台支援。
最後
網絡優化這個話題非常龐大,本文隻是在學習過程中從優化思路上列舉了目前業界常見的優化點,還有很多細節很多更深入的優化沒涉及到,網絡層實踐開發經驗不足,若有錯誤歡迎指出。
轉發自:http://www.importnew.com/27914.html