WEB加速,協議先行
大家在平時使用Web應用的時候,一定遇到了非常多的訪問問題,比如「無法接入網路」,比如「網頁一直在載入」,又比如「視頻卡頓」。那導致這些問題的背後原因有哪一些呢?
這裡我簡單羅列了一下,主要有如下幾類:
其中頁面大小主要是指頁面的體積,頁面的元素主要是指頁面元素類型以及數量,通常來講,一個頁面體積越大,頁面元素越多,動態交互的元素越多,那這個頁面的性能相應就會低一點。頁面相關的性能問題也是我們廣大前端同學(FE)優化的主戰場。
端配置是指用戶終端的硬體或者軟體配置,網路環境是指用戶所處環境的網路狀況,包括RTT,帶寬,丟包率等。這兩個因素主要是由用戶以及運營商決定的,WEB開發者很難施加影響。
今天想給大家重點介紹的就是網路協議,這是一個非常重要非常關鍵但又經常被大家忽視的因素。
那WEB請求主要會涉及到哪些網路協議呢?以現在逐漸流行的互聯網下一代協議HTTP2為例,詳細介紹一下一條HTTP2請求需要經過的協議棧。
上圖左邊是客戶端,右邊是數據中心,也就是服務端。
客戶端首先需要經過HTTP1.1協議,為什麼會是HTTP1.1協議呢?標題明明是HTTP2請求啊?這是因為HTTP2雖然是一個全新的協議,但是它沿用的大部分語義還是HTTP1.1。比如常用的Get 請求,Post請求,都是沿用的HTTP1.1的語義。對於js或者頁面來講,和HTTP1.1沒有變化。
HTTP1.1再接著就到了HTTP2協議,HTTP1.1的語義需要經過HTTP2frame的封裝,比如Get請求會經過HEADERs frame封裝,Post請求的body需要經過HTTP2 data frame的封裝。
再接著就來到TLS協議,傳輸加密層,這裡為什麼會有加密呢?事實上HTTP2 RFC7540規定,HTTP2有兩種實現,一種是H2,需要強制加密;還有一種H2C,這裡的C是指clear,就是明文,不需要加密的意思。雖然協議規定有兩種實現,但是現在所有的主流實現,包括所有的客戶端,chrome,firefox,ie,safari和所有的操作系統,都是強制使用的TLS加密。
加密層再往下就來到了大家用得很多的TCP層。
TCP再往下就到了IP網路層,以及乙太網鏈路層再經過運營商網路物理層等等到達服務端,然後經過相同的協議棧進行處理,就不多做介紹了。
那這麼多協議,我們需要關注哪些呢?圖中白色虛線往上的部分就是我們需要關注的部分,因為這部分才是客戶端能夠控制,能夠施加影響的協議。至於網路層及鏈路層等更低層的協議,客戶端是很難或者無法控制的。明確了我們需要關注的協議,那這些協議分別存在哪些問題呢?
還是從歷史最悠久使用最廣泛的HTTP1.1協議說起。
HTTP1.1協議最大的性能問題就是「單鏈接串列」。即在一個TCP鏈接上如果有多個請求,請求和請求之間必須按照次序串列發送。如圖中右邊所示。CSS和JS及JPG請求需要依次發送。
「單鏈接串列」相信大家也都比較清楚了,這裡再看下頭部未壓縮,會導致兩個問題:
1, 數據冗餘,重複傳輸,浪費帶寬。每次請求的Host,Cookie,UA等欄位都是一模一樣的,沒啥意義。
2, 影響訪問速度。特別是由於我們運營商網路上下行帶寬嚴重不對稱,上行帶寬往往是下行帶寬的10分之1甚至不到,比如通常說帶寬1M或者10M,上行帶寬往往只有100K甚至幾十K,我們統計的頭部平均大小是1500個位元組,有時候頭部變大就很可能在帶寬層面影響訪問性能。
HTTP1.1的問題其實大家都很清楚,也做了很多優化,那我們先來看一下HTTP1.1的優化策略。
概括來講,HTTP1.1的優化方向主要是兩點:
1, 增加並發連接數量。不管是單域名多TCP連接,還是域名分片,本質上都是希望通過建立多個並發連接,來提升並發請求效率。其實HTTP1.1嘗試過在單鏈接上並發請求來提升性能,比如pipelining,但由於存在隊頭阻塞,最後還是以失敗告終。
2, 減少請求數量。不管是CSS雪碧圖,還是datauri,圖片內聯等,都是希望將多個響應包裝到一個響應里返回。緩存也是希望在本地就能獲取到內容,不需要發出請求。
HTTP1.1的這些優化手段在HTTP1.1時代其實也取得了比較好的效果,也是被實踐證明的行之有效的方案,是在性能方面有很大的提升。
但是,隨著HTTPS以及HTTP2的快速普及,HTTP1.1的優化手段有很多已經失效了。
首先看一下背景,全站HTTPS趨勢是越來越明顯,不管是國內的BAT還是國外的FLAG,都已經支持了全站HTTPS。
HTTP2呢,2015年5月正式發布,目前也有13.7%的網站支持了HTTP2.
HTTPS性能上一個很大的特點或者說缺點就是連接成本高,而HTTP1.1的一個優化方向就是增加並發連接數量,如果單個連接成本變高了,並發連接的成本相應也會提高,所以還是依靠增加並發連接,反而在性能方面會起到副作用。
HTTP2最強大的特性就是支持多路復用,能夠將多個請求在一個鏈接上同時發送,實現並發請求。這樣HTTP1.1另外一個優化方向,減少請求數量,也就沒有太大意義了,因為減少請求數量,特別是內聯,可能會有降低緩存命中率,而且也會增加服務端的開發和維護成本。
所以從這個層面講,HTTP1.1的優化策略逐漸失效了。
那剛才提到高連接成本,HTTPS為什麼會有這麼高的連接成本呢?
左邊是一次HTTP1.1的請求過程,非常簡單,只需要通過三次握手建立TCP連接,然後就可以在這個TCP連接上發送HTTP1.1數據了。總共只需要2個RTT就能完成一次交互。
右邊是HTTPS的一次請求過程,非常複雜。簡單說一下過程。
1, 首先同樣需要建立TCP連接。
2, 然後開始發送HTTP1.1數據,這裡為什麼會是HTTP1.1的數據呢?因為如果是用戶主動輸入URL的情況下,絕大部分用戶不會主動輸入HTTPS,比如他訪問騰訊網,他可能會輸入http://qq.com或者www.qq.com或者http://www.qq.com,但是他很少會輸入https://www.qq.com,這樣我們就只能通過返回302強制用戶使用HTTPS。
3, HTTPS使用的是443埠,HTTP使用的是80埠,不同的埠必須要建立不同的TCP連接。這樣又需要三次握手來建立TLS的TCP連接。
4, 然後就可以在這個TCP連接上進行TLS握手了,首先完全握手階段一,主要是協商協議版本,密碼套件,返回證書。
5, 客戶端在獲取到證書後,校驗完證書的簽名和時間都沒有問題後,還是有可能要檢查這個證書的狀態,因為我們有可能主動撤銷了這個證書,也有可能是CA本身資料庫出了安全問題。所以又需要解析這個CA OCSP站點的域名。
6, 然後向這個CA站點的IP發起三次握手建立TCP連接。
7, 建立好TCP連接後,開始請求OCSP的內容,也是通過HTTP協議完成的。
8, OCSP校驗通過後,開始進行TLS完全握手階段二,這個階段主要是完成非對稱密鑰交換計算協商出對稱密鑰。
9, 至此,TLS握手完全結束,開始進行HTTP應用層的加密內容傳輸。
由此可見,一次HTTPS的請求有可能需要9個RTT,相比HTTP1.1多出了7個RTT。
7個RTT是什麼概念呢?最好的WIFI環境下,我們統計的平均RTT是70ms,7個RTT就是490ms,4G是100ms,3G是200ms。這還僅僅是一次請求的耗時,我們一個頁面往往有數十個請求,請求和請求之間還有可能依賴,有可能阻塞,這樣下來,一個HTTPS頁面增加幾秒鐘也是非常正常的現象。
如上描述的還只是HTTPS的的網路耗時,也就是協議的規定所必須要進行的網路交互。還有一部分耗時是計算耗時,比如客戶端需要校驗證書,計算密鑰,服務端也需要非對稱密鑰計算出對稱密鑰,還需要對內容進行對稱加解密。特別是移動端的CPU相對要弱,所以計算層面的耗時多出幾百毫秒也是比較正常的。
從上述原理分析,未經優化的HTTPS的速度要明顯慢於HTTP。
那HTTPS就等於HTTP+SLOW嗎?如果是,那除了上述網路協議的原理分析,還存在其他的性能瓶頸嗎?
接下去我們分別從線下測試及線下業務數據的角度來進一步分析。
線下模擬測試主要是關注移動端。移動端手機有兩個問題:
1, 手機屏幕比較小,不方便頁面性能分析和數據的查看。
2, 不方便執行我們自動控制腳本和數據分析腳本。
於是我們使用USB數據線將手機和PC連接起來,再通過chromeremote debug協議來控制手機不斷的重複地訪問我們所構造的不同的測試頁面。
所以一定要使用自動化的腳本去訪問,這樣才會非常方便地生產大量數據,包括各種應用場景。見過很多同事,拿著自己人工生成的幾十條數據就誤認為是結論,這樣的數據幾乎是沒有意義的。
另外就是要考慮消除誤差,一定要注意數據的周期性的同比,環比,數據量一般超過10000條才有一定的可信度。還有一個很大的誤差是WIFI網路非常不穩定,即使在我們辦公室裡面,比如騰訊大廈或者朗科大廈,WIFI也很容易丟包,延時抖動等。為了減少WIFI的影響,我們同樣的是使用USB線將手機和電腦連接,使得手機直接通過有線網路去請求頁面,因為有線網路相對要穩定很多,能很大程度地減少WIFI的影響。
還有一個工具就是trafficcontrol,因為我們真實的用戶網路環境是千差萬別的,有2G,3G,4G,WIFI,有不同的RTT,不同的帶寬,不同的丟包率。在辦公室是無法獲取這麼多真實的網路條件的,只能通過模擬,那 linux traffic control就能很好的模擬這些場景。
上述主要是介紹了一下我們線下模擬測試的經驗。但是線下測試的數據畢竟不能代表線上真實用戶的體驗,所以我們還需要對線上真實業務進行數據監控和分析。
線上速度監控的方案其實有非常多,我相信只要關注過速度或者前端性能的朋友都有一些比較成熟的做法,我就不多做介紹了。這裡我想重點分享的是我們基於服務端進行數據採集的方案。為什麼要基於服務端採集數據呢?主要是有兩大優勢 :
1, 服務端能採集到客戶端採集不到的數據。比如業務處理的整體時間,又比如跟協議相關的信息,包括是否TCP連接復用,TCP的RTT,是否TLS Session復用,TLS協議版本,密碼套件,TLS非對稱密鑰交換計算的時間,握手時間,HTTP2的頭部壓縮比等。
2, 開發成本低。因為有一些數據客戶端如果有網路讀寫能力,也能獲取到一些底層信息,但是客戶端的操作系統種類多,有iOS,android,windows等,不同的系統,不同的版本,開發方案肯定是不同的。但是服務端一次開發就可以適配所有客戶端,因為服務端將統計的數據,放到cookie里返回給了客戶端,客戶端只需要通過JS就能獲取到cookie里的信息,然後JS又將自己採集到的和頁面相關信息匯聚,最後統一上報到報表分析平台。
那採集了這麼多協議相關的信息,拿到了這麼多數據,我們該如何分析呢?
上表只是截取了我們很少的幾個維度,其中第一列是我們的統計維度,比如tcp_reuse表示TCP連接復用,TLSv1.2表示TLS協議版本是1.2,ecdhe_rsa_aes128_gcm_sha256表示密碼套件等。
第一行中的其他數據表示我們的監控指標,比如start_load表示開始載入的時間,active表示頁面可以活動的時間,req_time表示業務處理的時間等。
上述這些單一維度又可以進行多維度的整合,比如這行黃色的文字所示「騰訊X5內核瀏覽器在4G網路下使用HTTP2並且是TLS1.2協議並且使用ECDHE並且沒有復用tls session的首屏時間是多少?」
然後我們又清晰地看到它使用TLS1.1,1.0,以及其他密碼套件的一些性能數據,為什麼快,為什麼慢,通過這樣的多維度對比,一目了然。
也就是說我們收集了這麼多的數據,並不是用來好看的,也不是僅僅用來做報表的,最終是為了找到其中的性能瓶頸,從而為我們的速度優化尋找方向。
那我們主要有哪些方向呢?
這裡主要從非前端的角度講述一下我們的優化方向,主要是這三大塊:
1, 協議。
2, 資源。
3, 用戶行為。
首先我們來看一下協議,這次我們從最底層的TCP協議說起。
TCP協議在性能方面最顯而易見的問題就是它需要三次握手才能建立一個連接,才能發送數據,浪費了一個RTT。
那TCP fastopen的思路就是在SYN包發出的同時將應用層數據一起發出來,減少三次握手對延時的影響。
TFO的大概流程是這樣的,首先也需要三次握手才能建立連接,所不同的是,服務端返回syn+ack的時候還會返回一個cookie。這樣在下一次建立連接時,客戶端就會發出syn包的同時,將cookie 和應用層數據一起發出來,減少了這個RTT的浪費。
TFO的優化效果其實也很明顯,我們統計80分位的數據提升了100毫秒。它的缺點也比較明顯,就是需要客戶端的操作系統支持,比如iOS9+以及linux kervel3.7+才支持TFO,windows都還不支持。
TCP另外一個優化就是增加初始擁塞窗口,由3個增加到10個。這個其實也是Google很早就提出的一個方案,也被標準內核採納了,所以我就不多做介紹了。包括Google最近提出的BBR演算法,對於長肥管道也有一定的效果。
總得來說,在TCP層面進行優化的空間不大,成本很高。因為它需要操作系統,需要內核的支持。如果僅僅是服務端開發倒還好,關鍵是需要用戶,甚至需要廣大的網路中間設備的系統和協議棧支持,這樣就會導致部署阻力非常大。
接下去我們看一下TLS協議的優化。
TLS協議最大的性能問題也是它的握手。所以優化的目標也非常明確,就是減少完全握手,提升簡化握手的比例。
協議層面提供了兩種機制,sessionid和session ticket,我相信接觸過的同學都非常清楚,網上的資料也非常多,關於原理和過程我就不多做介紹了。
這裡我主要是分享兩點:
1, 通過提升簡化握手比例,iOSQzone的SSL握手時間提升了50%,從200ms節省到了100ms。
2, 雖然sessionticket是一種更加優秀的機制,因為它不需要服務端做緩存,但是iOS目前還不支持session ticket,要想實現簡化握手,必須要支持session id,並且最好是實現分散式session cache來提升簡化握手比例。
然後我們再來看一下完全握手,因為有很多場景下必須要進行完全握手,比如用戶第一次打開瀏覽器或者App,用戶關閉tab頁面再打開,用戶手機或者系統重啟等,都需要進行完全握手,因為前面提到的sesison ticket還是session id都是基於內存的,客戶端重啟之後再發起握手默認就無法攜帶上這些信息,必須進行完全握手。
那針對完全握手該如何優化呢?
優化思路類似TFO,即在完全握手的第二個階段,即密鑰交換階段,提前發送應用層數據,節省這一個RTT對性能的影響。
上圖左邊是普通握手,可以看出必須要進行四次握手,兩個RTT之後才能發送綠色的HTTP加密數據。
上圖右邊就是false start,搶跑的意思。在第二階段,clientKeyExchange消息發出的時候,將HTTPGET的應用層數據加密發出來了。相當於節省了一個RTT。
那如何支持false start呢?很簡單。因為現在最新的客戶端都已經支持了這個特性,所以對於服務端來廛,我們只需要將支持PFS(完美前向密碼)演算法的密碼套件配置在前面就行了,比如ECDHE,DHE演算法配置在最前面。協商好密碼套件後,客戶端就能提前發送數據,實現false start。
False start對完全握手的優化效果也很明顯,大概提升了30%。
接下來我們再看一下OCSP的問題。OCSP是在線證書狀態檢查協議,這個檢查和證書本身的簽名校驗不是一回事。
因為有一些情況,單純檢驗簽名是發現不了的。比如我們申請了一張有效期一年的證書,但不幸的是,申請下來的第一個月,私鑰被內部人員泄露了,或者CA本身的資料庫被黑客攻擊了,我們需要主動撤銷這張證書的信任關係。那就只能主動告訴CA這張證書不安全,然後客戶端自己再去CA那邊查詢一下。因為這個時候證書本身的簽名是沒有問題的,如果不去額外查一下,在證書本身過期之前,永遠也發現不了證書被撤銷的事實。
OCSP的過程發生在客戶端接收到serverhello和certificate消息後,這個時候它會根據證書里的OCSP域名,發起OCSP請求,如上圖左邊所示。
OCSP stapling的意思,簡單來說就是服務端代理CA實現的OCSP內容的簽發。服務端會提前向CA站點請求好OCSP內容,並保存在本地,在握手的時候,將OCSP內容和證書一起發送給客戶端,這樣客戶端就不需要自己主動去請求CA查詢OCSP內容了。
這樣看來OCSP Stapling至少節省了三個RTT,效果應該非常不錯。但事實上,OCSP Stapling的效果並不會特別突出,因為客戶端有緩存。一般來講會有7天,也就是說客戶端7天中才會查詢一次OCSP。對於一個用戶經常訪問的頁面來講,這個概率可能只有千分之一甚至萬分之一。所以對用戶的訪問體驗來講,提升的效果也比較有限。
然後我們再看一下dynamicrecord size。為什麼需要做這個動態調整呢?是因為TLS協議本身的HOL(隊頭阻塞)。
Record是TLS協議處理的最小單位,最大不能超過16K,一些伺服器比如Nginx默認的大小就是16K。由於一個record必須經過數據一致性校驗才能進行加解密,所以一個16K的record,就算丟了一個位元組,也會導致已經接收到的15.99K數據無法處理,因為它不完整。
比如上圖右邊所示,假設一個record需要6個TCP segment傳輸完成,如果最後一個segment丟了,那麼上層應用程序必須HANG在那裡等,無法繼續處理。
上述就是TLS協議層面的隊頭阻塞,那如何解決呢?也有兩個方案:
1, Nginx高版本支持一個配置指令ssl_buffer_size,可以將它設置成4K,這樣就算有HOL,影響的也只是4K數據,而不是之前的16K。
2, 更好的一個方案是動態調整大小,思路類似tcp 的slow start。在TLS連接剛剛建立的時候,由於不知道網路速率,可以將record設置得小一點,比如1K,當發送速度逐漸提上來之後,再將這個record size設置成16K。這個方案也已經有開源的patch,是cloud flare提供的,大家有興趣可以關注一下。
剛才提到的一些TLS優化特性都是針對TLS1.2及其之前的協議版本。接下去我們看一下TLS1.3協議。這是一個具有革命性的創造性的極具里程碑意義的TLS協議。
它現在遲遲沒有發布的一個討論焦點就是它到底該叫TLS1.3還是TLS2.0。在性能方面最大的提升就是能夠實現1RTT的完全握手,能夠實現0RTT的簡化握手。
上圖左邊就是1RTT的完全握手,右邊就是0RTT的簡化握手,也就是說應用層數據可以握手消息一起發出來,而且都是經過加密的。
關於TLS1.3協議的原理和0RTT的詳細過程我就不做詳細描述了,因為還沒有正式發布,那這裡為什麼又給大家介紹呢?因為如果大家想嘗鮮的話,現在就可以體驗了。
Openssl1.1.1以及Nginx 1.13.0目前已經支持TLS1.3的最新草稿draft20。也有一些客戶端支持了TLS1.3,比如firefox。大家如果有自己的客戶端,現在就可以參考這些實現進行集成。
剛才我們介紹的是TLS協議,接下去我們再往上看,HTTP協議。
之前提到過需要302跳轉強制用戶使用HTTPS。那能不能減少這個跳轉呢?HSTS就是這個作用。這是一個HTTP的Header,客戶端接收到這個頭部後,就會在接下去指定的時間內,默認只發起HTTPS請求。不管用戶輸入qq.com, www.qq.com還http://www.qq.com,瀏覽器都會在本地進行跳轉,直接發起HTTPS請求。
就算返回的HTML里包含有HTTP資源,瀏覽器也會將它們全部替換成HTTPS資源。
不過HSTS還是有一個安全風險,因為通常來講它都是通過HTTP1.1協議返回的,所以很容易被中間者劫持,直接被幹掉,這樣客戶端就可能永遠也接收不到HSTS的頭部,也就不會發起HTTPS請求了。
為了解決這個問題,chrome提供了一個preloadlist的機制,大家可以給上圖所示的網站申請,將自己的網站域名加入到preload list里,這樣不需要返回HSTS,chrome也會默認使用HTTPS來訪問你的網站。
然後我們再來看一下SPDY和HTTP2,這裡為什麼要提一下SPDY呢,主要是兩個原因:
1, HTTP2的大部分特性,除了HPACK頭部壓縮演算法,都是沿用自SPDY,可以說SPDY是HTTP2的鼻祖。現在越來越多的人只知道HTTP2,不知道SPDY,我這裡介紹一下主要是為了向它致敬。
2, 現在還有很多的客戶端只支持SPDY,比如Android4.4.4以前,以及iOS現在都支持SPDY,為了兼容一些老的客戶端,提升它們的性能,我們騰訊雲的服務端也是同時支持SPDY和HTTP2。
上圖列的幾個HTTP2特性是大家都比較清楚的特性,其中多路復用是HTTP2最強大的特性,它能將多個請求在一個連接上並發地發出來,同時請求和請求之間在協議層面可以沒 任何依賴,當然也可以有依賴。也就是說誰先處理完成誰就可以先返回,不會影響其他請求的處理。
HTTP1.1的pipelining就不行,比如上圖右邊,如果在同一個連接上發起四個請求,那四個響應必須按照順序返回,其中一個處理慢了或者丟失了,都會導致四個請求全都無法處理,也就是http1.1 pipelining的隊頭阻塞。
前面幾個HTTP2特性大家比較清楚,這裡我再重點介紹一下兩個大家可能不太清楚的特性,一個是頭部壓縮,從字面很好理解,就是壓縮了頭部大小,提升了傳輸效率。但壓縮比真的如官方頁面宣傳的有90%嗎?
我們通過實驗發現,在一個連接上發起第一次請求時,壓縮只有30%,發起第二次請求時,壓縮比能達到60%,一直到第三次請求以及之後的請求時,壓縮比才能達到將近90%。為什麼會這樣呢?因為SPDY頭部壓縮是基於zlib的,HTTP2是基於Hpack的壓縮演算法,它們都是利用狀態空間的重複信息進行壓縮,也就是說信息越冗餘越重複,壓縮比才會越高。
這帶給我們的啟發就是,在用戶發起真正的請求前,我們利用JS提前發送兩個空請求,積累重複的頭部數據,當用戶發起真正的請求時,已經是這個連接上的第三個請求了,這樣用戶請求的頭部壓縮比一下就能達到90%,有利於提升用戶訪問速度。
然後再來看一下serverpush。由於Nginx不支持server push,這個功能目前在國內用得還比較少,但確實很有用。
比如客戶端請求一個html,正常來講它需要解析完DOM後再請求css和png,這裡至少會有2個RTT。但是如果支持serverpush,我們在服務端配置一個link頭部,這樣伺服器在接收到html請求後就知道將另外兩個資源css和png 一起返回給客戶端,不需要客戶端發起額外的請求。
這就是serverpush未發先至的作用,和inlining有點類似,但是相比inlining有兩個好處:
1, 有利於緩存。因為inlining在html里,不僅增加了html的體積,而且html一般來講是不會緩存的。
2, 減少開發成本,css 雪碧圖也好還是圖片內聯,服務端都有一定的開發成本,有些開發甚至是反模式的,需要精確地位置調整和屏幕適配。
再分享一下HTTP2的實踐建議。
1, 使用一個連接,或者使用盡量少的連接。為什麼呢?有三個好處:
a) 連接少意味著更少的連接建立成本,之前也提到了,HTTPS的連接成本很高。
b) 能夠實現更高的壓縮比,因為數據都在一個連接上,提供的冗餘信息更豐富,有利於壓縮。
c) 能夠更好地利用TCP的特性。因為TCP的很多特性,包括滑動窗口,擁塞控制都是基於一個連接的,如果連接數量多,特別是網路擁塞的時候,很容易放大擁塞係數,加劇擁塞。
2, 使用更少的域名。一方面能夠減少域名解析的時間,另外一方面也能建立更少的連接,作用和第一點類似。
3, 如果一定要使用多個域名,那麼盡量保證多個域名解析到相同IP,並且使用了相同的證書。這樣也能方便瀏覽器復用相同的連接,比如chrome就會作這樣的智能判斷。
4, 靈活運用serverpush,代替inlining。
5, HTTP2只支持TLS1.2及之後的版本(TLS1.3)。而且只有TLS1.2的部分cipher suite才能使用HTTP2。所以如果大家想使用HTTP2,一定要注意配置好TLS1.2協議,參考RFC7540的規範,配置好密碼套件。
6, HTTP2不是萬能的,如果你的頁面很簡單,比如只有幾個元素,如果還是有性能問題,那也不能寄希望於HTTP2。HTTP2最強大的特性是多路復用,還是適合於解決多元素多請求的場景。
最後我們再來看一下預建連接。所謂的預建連接就是在用戶發起正直的請求前將連接提前建立好。這樣當用戶發起請求時,由連接建立所導致的開銷成本,用戶都是感覺不到的。
所以預建連接可以說是一種最簡單,最有效的方案。因為之前提到的一系列方案,就算是0RTT握手,也會有一些數據校驗和計算的工作。預建連接的效果也非常明顯,對性能的提升至少是400ms以上。
那如何預建連接呢?主要有兩個方法:
1, 通過link標籤和頭部告訴瀏覽器提前建立另外一個資源的連接。不過還有很多瀏覽器或者一些歷史版本不支持這個特性。
2, 通過頁面的JS給另外一個資源發送請求,提前建立連接。
預建連接也可以根據具體的用戶場景和用戶行為來建立。比如:
1, 首頁提前預建子頁面的連接。當用戶打開百度首頁的時候,它通常會發起搜索,那我們可以提前給搜索結果頁面建立連接。
2, 根據用戶行為預測。比如用戶進入QQ空間首頁時,我們可以根據用戶的瀏覽習慣,他是經常訪問QQ相冊,還是經常訪問QQ商城來預建不同的連接。
預建好的連接也有可能會超時斷開,比如HTTPS的超時時間是1分鐘,HTTP2有可能是3分鐘,那過了幾分鐘後連接就斷開了,用戶再次發起請求時又需要建立新連接,如何避免這種情況呢?
可以使用長連接保持。即我們使用JS周期性比如每分鐘發起一次長連接保持的請求,stgw就提供一個空頁面,JS訪問這個頁面我們返回1個位元組,作用就是為了維持住這個連接,不讓它中斷。
總的來說,HTTPS的訪問速度是可以超越HTTP1.1的,這裡面最核心的一點是,HTTPS可以使用HTTP2,可以多路復用,而HTTP1.1不行。
上圖的數據是兩年前我們H5 QQ空間優化的效果,數據雖然有點老,不過思路和優化方法是一致的,沒有過時。
前面提到了很多HTTP2的特性,性能也很強大,那HTTP2是未來嗎?或者更準確地說,HTTP2是下一個十年,最有性能優勢,最具有統治力的WEB協議嗎?
可以說是。因為它的許多特性,包括多路復用,頭部壓縮,server push,優先順序等,設計得十分先進,性能也十分優良,解決了許多WEB性能問題。
也可以說不是,為什麼?因為當前的HTTP2協議是構建在TCP和TLS之上的,由此導致了一系列問題:
1, TCP連接耗時。比如需要TCP三次握手才能建立連接,就算是有了TFO,也需要操作系統才能支持,有許多系統目前也不支持TFO。而且TFO本身,在第一次獲取Cookie時,也需要一次額外的RTT才能實現接下去的快速握手。
2, TLS連接耗時,當前的TLS1.2至少需要1個RTT才能建立TLS連接。
3, TLS安全問題,TLS目前並沒有針對TCP頭部進行一致性校驗,從而存在TCP頭部被篡改的風險,比如修改滑動窗口數,修改TCP序列號等。
4, 加劇TCP隊頭阻塞。TCP為了實現可靠性和數據的有序性,發生segment丟包後需要重傳,就算丟包序列號之後的包提前到達了,也需要等待丟失的包重傳才能通知應用層來讀取數據。這就是TCP的隊頭阻塞問題,而HTTP2的多路復用,加劇了TCP的隊頭阻塞,因為一條連接上同時發送的數據變多了。隊頭阻塞的影響也就更嚴重了。
5, 重傳的模糊性問題。由於TCP重傳segment的序列號和原始segment的序列號相同。在判斷該segment及後續segment是否需要重傳的時候,很容易迷糊。
6, 擁塞控制,需要操作系統的支持,升級成本高。
以上種種,影響了HTTP2的性能,所以從這個角度來看,也可以說HTTP2並不是未來最有性能優勢的協議,那什麼才是呢?我覺得最有競爭力的一個協議就是QUIC。
讓我們擁抱QUIC。那什麼是QUIC協議呢?簡單來說就是使用UDP實現的HTTP2.它具體以下特性:
1, 繼承了HTTP2的大部分特性,包括多路復用,頭部壓縮,server push,優先順序等。
2, 當前支持0RTT握手,等TLS1.3發布後,也會使用TLS1.3的0RTT握手協議。
3, 使用UDP傳輸,沒有TCP連接建立的耗時。
4, 針對IP Packet進行加密,減少了隊頭阻塞的程度。
5, 針對UDP頭部進行一致性校驗,就算是修改了UDP頭部也能及時發現。
由於時間關係,關於QUIC的原理和詳細功能我就不再做介紹了。感興趣的同學可以關注我們後續的報道,如果有機會,下一次架構師峰會,也可以專門給大家重點分享。
推薦閱讀:
※從零部署一個https網站
※看到網址前的小嘆號,隱私就處在危險的境地
※TLS完全指南(一):TLS和安全通信
※知乎都全站 HTTPS 好久了, 你還好意思不懂 HTTPS?
※搭建基於 Nginx 的 Https 站點