本篇文章是對bang神的文章移動App網絡優化概述進行的總結,文章中也加了的一些本身的理解與擴展。html
咱們每次在作業務作網絡請求的時候,想必每一個人都思考過如何進一步優化網絡請求吧,好比這三點包括:對基於瀏覽器的前端開發來講,網絡這塊能作的事情不多,但對於客戶端 APP 來講,整個網絡請求過程是自由控制的,能夠作不少事情,不少大型 APP 都針對這三個問題作了不少網絡層的優化,一些新的網絡層協議像 HTTP2 / QUIC 也是在這些方面進行了很多優化,在這裏邊學習邊整理,大體列舉一下常見的作法。前端
正常一條網絡請求須要通過的流程是這樣:ios
這裏有明顯的三個優化點:git
依次來分析每一個優化點咱們能作什麼github
DNS 完整的解析流程很長,會先從本地系統緩存取,若沒有就到最近的 DNS 服務器取,若沒有再到主域名服務器取,每一層都有緩存,但爲了域名解析的實時性,每一層緩存都有過時時間,這種 DNS 解析機制有幾個缺點:算法
爲了解決這些問題,就有了 HTTPDNS,這裏有iOS版本接入文檔HTTPDNS iOS客戶端接入文檔 ,原理很簡單,就是本身作域名解析的工做,經過 HTTP 請求後臺去拿到域名對應的 IP 地址,直接解決上述全部問題:json
其他細節就很少說了,HTTPDNS 優勢這麼多,幾乎成爲中大型 APP 的標配。至此解決了第一個問題 — DNS 解析耗時的問題,順便把一部分安全問題 — DNS 劫持也解決了。瀏覽器
第二個問題,鏈接創建耗時的問題,這裏主要的優化思路是複用鏈接,不用每次請求都從新創建鏈接,如何更有效率地複用鏈接,能夠說是網絡請求速度優化裏最主要的點了,而且這裏的優化仍在演進過程當中,值得了解下。緩存
HTTP 協議裏有個 keep-alive,HTTP1.1默認開啓,必定程度上緩解了每次請求都要進行TCP三次握手創建鏈接的耗時。原理是請求完成後不當即釋放鏈接,而是放入鏈接池中,若這時有另外一個請求要發出,請求的域名和端口是同樣的,就直接拿出鏈接池中的鏈接進行發送和接收數據,少了創建鏈接的耗時。安全
**實際上如今不管是客戶端仍是瀏覽器都默認開啓了keep-alive,對同個域名不會再有每發一個請求就進行一次建連的狀況,純短鏈接已經不存在了。**但有個問題,就是這個 keep-alive 的鏈接一次只能發送接收一個請求,在上一個請求處理完成以前,沒法接受新的請求。若同時發起多個請求,就有兩種狀況:
對這個問題,新一代協議 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 一致。
HTTP2 的多路複用看起來是完美的解決方案,但還有個問題,就是隊頭阻塞,這是受限於 TCP 協議,TCP 協議爲了保證數據的可靠性,若傳輸過程當中一個 TCP 包丟失,會等待這個包重傳後,纔會處理後續的包。HTTP2的多路複用讓全部請求都在同一條鏈接進行,中間有一個包丟失,就會阻塞等待重傳,全部請求也就被阻塞了。
對於這個問題不改變 TCP 協議就沒法優化,但 TCP 協議依賴操做系統實現以及部分硬件的定製,改進緩慢,因而 GOOGLE 提出 QUIC 協議,至關於在 UDP 協議之上再定義一套可靠傳輸協議,解決 TCP 的一些缺陷,包括隊頭阻塞。QUIC協議雖然是基於UDP,但它不但具備TCP的可靠性、擁塞控制、流量控制等,QUIC 協議相對於 HTTP2 最大的優點是對TCP隊頭阻塞的解決,另外,QUIC協議具備TLS的安全傳輸特性,實現了TLS的保密功能,同時又使用更少的RTT創建安全的會話。
第三個問題,傳輸數據大小的問題。數據對請求速度的影響分兩方面,一是壓縮率,二是解壓序列化反序列化的速度。目前最流行的兩種數據格式是 json 和 protobuf,json 是字符串,protobuf 是二進制,即便用各類壓縮算法壓縮後,protobuf 仍會比 json 小,數據量上 protobuf 有優點,序列化速度 protobuf 也有一些優點,這二者的對比就不細說了。能夠看此文章protobuf 在iOS上的實踐來進一步瞭解protobuf
壓縮算法多種多樣,也在不斷演進,最新出的 Brotli 和Z-standard實現了更高的壓縮率,Z-standard 能夠根據業務數據樣本訓練出適合的字典,進一步提升壓縮率,目前壓縮率表現最好的算法。
除了傳輸的 body 數據,每一個請求 HTTP 協議頭的數據也是不可忽視,HTTP2 裏對 HTTP 協議頭也進行了壓縮,HTTP 頭大可能是重複數據,固定的字段如 method 能夠用靜態字典,不固定但多個請求重複的字段例如 cookie 用動態字典,能夠達到很是高的壓縮率,這裏有詳細介紹。
經過 HTTPDNS,鏈接多路複用,更好的數據壓縮算法,能夠把網絡請求的速度優化到較不錯的程度了,接下來再看看弱網和安全上能夠作的事情。
手機無線網絡環境不穩定,針對弱網的優化,微信有較多實踐和分享,包括:
針對弱網的這些細緻優化未成爲標準,系統網絡庫沒有內置,不過前兩個客戶端優化微信的開源網絡庫 mars 有實現,如有須要可使用。
標準協議 TLS 保證了網絡傳輸的安全,前身是 SSL,不斷在演進,目前最新是 TLS1.3。常見的 HTTPS 就是 HTTP 協議加上 TLS 安全協議。
安全協議歸納性地說解決兩個問題:1.保證安全 2. 下降加密成本
在保證安全上:
下降加密成本上:
這些點涉及的細節很是多,對 TLS 的介紹有一篇雄文,說得很詳細,在此推薦。
目前基本主流都支持 TLS1.2,iOS 網絡庫默認使用 TLS1.2,Android4.4 以上支持 1.2。TLS1.3 iOS 還處於測試階段,Android 未查到消息。對於普通 APP,只要正確配置證書,TLS1.2 已經能保證傳輸安全,只是在建連速度上會有所損耗,有一些大型 APP 像微信就自行實現了 TLS1.3 的部分協議,早一步全平臺支持。
bang的文章 讓我對網絡層更深一步的理解了,更加全面的瞭解了網絡請求過程當中的每個細節以及如何優化,更如bang說的,網絡優化這個話題很是龐大,這僅僅是網絡優化的冰山一角