導語 | 當產品的用戶量不斷翻番時,需求會倒逼着你優化HTTP協議。那麼,要想極限優化HTTP性能,應該從哪些維度出發呢?本文將由TVP陶輝老師,爲你們分享四個全新維度。「TVP思享」專欄,凝結大咖思考,匯聚專家分享,收穫全新思想,歡迎長期關注。(編輯:雲加社區 尾尾)
做者簡介:陶輝,騰訊雲最具價值專家(TVP),杭州智鏈達數據有限公司 CTO及聯合創始人,曾就任於阿里雲、騰訊、華爲、思科等公司,著有暢銷書《深刻理解Nginx:模塊開發與架構解析》,與極客時間合做暢銷視頻課程《Web協議詳解與抓包實戰》、《Nginx核心知識100講》。css
不管你在作前端、後端仍是運維,HTTP都是不得不打交道的網絡協議。它是最經常使用的應用層協議,對它的優化,既能經過下降時延帶來更好的體驗性,也能經過下降資源消耗帶來更高的併發性。
但是,剛學HTTP不久的同窗,很難全面說出HTTP協議的全部優化點。而當你要準備大廠的面試,或者要加入一個快速發展的項目的時候,你就有必要了解這一方面的內容了。由於當產品的用戶量不斷翻番時,需求會倒逼着你優化HTTP協議。html
本文是陶輝老師在2019年GOPS全球運維大會上海站的演講從新提煉後的總結,但願能從四個全新的維度,帶你覆蓋絕大部分的HTTP優化技巧。這樣,即便不須要極致方法去解決當前的性能瓶頸,也能知道優化方向在哪,當需求來臨時,可以到Google上定向查閱資料。前端
第一個維度,是從編碼效率上,更快速地把消息轉換成更短的字符流。這是最直接的性能優化點。面試
若是你對HTTP/1.1協議作過抓包分析,就會發現它是用「whitespace-delimited」方式編碼的。用空格、回車這些符號來編碼,是由於HTTP在誕生之初追求可讀性,這樣更有利於它的推廣。算法
然而在當下,這種編碼方式已經嚴重影響性能了,因此2009年Google推出了基於二進制的SPDY協議,大幅提高了編碼效率。2015年,稍作改進後它被肯定爲HTTP/2協議,如今50%以上的站點都在使用它。chrome
這是編碼優化的大方向,包括即將推出的HTTP/3。後端
然而這些新技術究竟是怎樣提高性能的呢?咱們須要拆開了來看,先從數據的壓縮談起。瀏覽器
抓包看到的是數據,它並不等於信息。數據實際上是信息和冗餘數據之和,而壓縮技術,就是儘可能地去除冗餘數據。緩存
壓縮分爲無損壓縮和有損壓縮。針對圖片、音視頻,咱們天天都在與有損壓縮打交道。好比,當瀏覽器只須要縮略圖時,就沒有必要浪費帶寬傳輸高清圖片。安全
而高清視頻作過有損壓縮後,在肉眼沒法分清時,已經被壓縮了上千倍。這是由於,聲音、視頻均可以作增量壓縮。
還記得曾經的VCD嗎?當光盤有劃痕時,整張盤都沒法播放,就是由於那時的視頻作了增量壓縮,並且關鍵幀太少,致使關鍵幀損壞時,後面的增量幀所有沒法播放了。
再來看無損壓縮,你確定用過gzip,它讓http body實現了無損壓縮。肉眼閱讀壓縮後的報文全是亂碼,但接收端解壓後,能夠看到發送端的原文。然而,gzip的效率其實並不高,以Google推出的brotli作對比,你就知道它的缺陷了:
評價壓縮算法時,咱們重點看兩個指標:壓縮率和壓縮速度。上圖中能夠看到,不管用gzip 9個壓縮級別中的哪個,它的壓縮率都低於brotli(相比gzip,壓縮級別它還能夠配置爲10),壓縮速度也更慢。
因此,若是能夠,應該儘快更新你的gzip壓縮算法了。
說完對body的壓縮,再來看HTTP header的壓縮。對於HTTP/1.x來講,header就是性能殺手。特別是當下cookie氾濫的時代,每次請求都要攜帶幾個KB的頭部,很浪費帶寬、CPU、內存!
HTTP2經過HPACK技術大幅度下降了header編碼後的體積,這也是HTTP3的演進方向。HPACK究竟是怎樣實現header壓縮的呢?
HPACK經過Huffman算法、靜態表、動態表對三種header都作了壓縮。好比上圖中,method GET存在於靜態表,用1個字節表示的整數2表達便可;user-agent Mozilla這行頭部很是長,當它第2次出現時,用2個字節的整數62表示便可;即便它第1次出現時,也能夠用Huffman算法壓縮Mozilla這段很長的瀏覽器標識符,能夠得到最多5/8的壓縮率。
靜態表中只存放最多見的header,有的只有name,有的同時包括name和value。靜態表的大小頗有限,目前只有61個元素。
動態表應用了增量編碼的思想,即,第1次出現時加入動態表,第2次出現的時候,傳輸它在動態表中的序號便可。
Huffman編碼在winrar等壓縮軟件中廣爲使用,但HPACK中的Huffman有所不一樣,它使用的是靜態huffman編碼。
它統計了互聯網上幾年內的HTTP頭部,按照每一個字符出現的機率,重建huffman樹。這樣,根據規則,出現次數最多的a、c、e或者一、二、3這些字符就只用5個bit位表示,而不多出現的字符則用幾十個bit位表示。
說完header,再來看http body的編碼。這裏舉3個例子:
第一:只有幾十字節的小圖標,沒有必要用獨立的HTTP請求傳輸,根據RFC2397的規則,能夠把它直接嵌入到HTML或者CSS文件中,而瀏覽器在解析時會識別出它們,就像下圖中的頭像:
第二:JS源碼文件中,可能有許多小文件,這些文件中也有許多空行、註釋,經過WebPack工具,先在服務器端打包爲一個文件,並去除冗餘的字符,編碼效果也很好。
第三:在表單中,能夠一次傳輸多個元素,好比既有複選框,也能夠有文件。這就減小了HTTP請求的個數。
可見,HTTP協議從header到 body,都有許多編碼手段,可讓傳輸的報文更短小,既節省了帶寬,也下降了時延。
編碼效率優化完後,再來看「信道」。這雖然是通信領域的詞彙,但用來歸納HTTP的優化點很是合適,這裏就借用下了。
信道利用率包括3個優化點,第一個優化點是多路複用!高速的低層信道上,能夠跑許多低速的高層信道。
好比,主機上只有一塊網卡,卻能同時讓瀏覽器、微信、釘釘收發消息;一個進程能夠同時服務幾萬個TCP鏈接;一個TCP鏈接上能夠同時傳遞多個HTTP2 STREAM消息。
其次,爲了讓信道有更高的利用率,還得及時恢復錯誤。因此,TCP工做的很大一部分,都是在及時的發現丟包、亂序報文,並快速的處理它們。
最後,就像經濟學裏說的,資源老是稀缺的。有限的帶寬下,如何公平的對待不一樣的鏈接、用戶和對象呢?
好比下載頁面時,若是把CSS和圖片以同等優先級下載就有問題,圖片晚點顯示不要緊,但CSS沒獲取到頁面就沒法顯示。
另外,傳輸消息時,報文頭報並不承載目標信息,但它又是必不可少的,如何下降這些控制信息的佔比呢?
咱們先從多路複用談起。廣義上來講,多線程、協程都屬於多路複用,但這裏我主要指HTTP2的stream。由於HTTP協議被設計爲client先發request,server才能回覆response,這樣收發消息,是沒辦法跑滿帶寬的。
最有效率的方式是,發送端源源不斷地發請求、接收端源源不斷地發響應,這對於長肥網絡尤其有效:
HTTP2的stream就是這樣複用鏈接的。咱們知道,chrome對一個站點最多同時創建6個鏈接,而有了HTTP2後,只須要一個鏈接就能高效的傳輸頁面上的數百個對象。
我特地讓個人我的站點www.taohui.pub同時支持HTTP1和HTTP2,下圖是鏈接視角上HTTP2和HTTP1的區別。
熟悉chrome Network網絡面板的同窗,確定很熟悉waterfall,它能夠幫助你分析HTTP請求到底慢在哪裏,是請求發出的慢,仍是響應接收的慢,又或者是解析得太慢了。下圖仍是個人站點在waterfall視角下的對比。
從這兩張圖能夠看出,HTTP2全面優於HTTP1。
再來看網絡錯誤的恢復。在應用層,lingering_time經過延遲關閉鏈接來避免瀏覽器因RST錯誤收不到http response,而timeout則是用定時器及時發現錯誤並釋放資源。
在傳輸層,經過timestamp=1可讓TCP更精準的測量出定時器的超時時間RTO。固然,timestamp還有一個用途,就是防止長肥網絡中的序列號迴繞。
什麼是序列號迴繞呢?咱們知道,TCP每一個報文都有序列號,它不是指報文的次序,而是已經發送的字節數。因爲它是32位整數,因此最多能夠處理232也就是4.2GB的飛行中報文。
像上圖中,當1G-2G這些報文在網絡中飛行時間過長時,就會與5G-6G報文重疊,引起錯誤。
網絡錯誤還有不少種,好比報文的次序也是沒法保證的。打開tcp_sack能夠減小亂序時的重發報文量,下降帶寬消耗。
用Chrome瀏覽器直接下載大文件時,網絡很差時,一出錯就得所有重傳,體驗不好。
改用迅雷下載就快了不少。這是由於迅雷把大文件拆成不少小塊,能夠多線程下載,並且每一個小塊出錯後,從新下載這一個塊便可,效率很高。
這個斷點續傳、多線程下載技術,就是HTTP的Range協議。若是你的服務是緩存,也可使用Range協議,好比Nginx的Slice模塊就作了這件事。
實際上對於網絡錯誤恢復,最精妙的算法是擁塞控制,它能夠全面提高網絡性能。有同窗會問,TCP不是有流量控制,爲何還會發生網絡擁塞呢?這是由於,TCP鏈路中的各個路由器,處理能力並不互相匹配。
就像上圖,R1的峯值網絡是700M/s,R2的峯值網絡是600M/s,它們都須要經過R3才能到達R4。然而,R3的最大帶寬只有1000M/s!當R一、R2中的TCP全速使用各自帶寬時,就會引起R3丟包。擁塞控制就是解決丟包問題的。
自1982年TCP誕生起,就在使用傳統的擁塞控制算法,它是發現丟包後再剎車減速,效果很很差。
爲何呢?你能夠觀察下圖,路由器中會有緩衝隊列,當隊列爲空時,ping的時延最短;當隊列將滿時,ping的時延很大,但還未發生丟包;當隊列已滿時,丟包纔會發生。
因此,當隊列出現積壓時,丟包沒有發生。雖然此時峯值帶寬不會減小,但網絡時延變大了,這是要避免的。
而測量驅動的擁塞控制算法,就在隊列剛出現積壓這個點上開始剎車減速。在當今內存愈來愈便宜,隊列愈來愈大的年代,新算法尤其有效。
當Linux內核更新到4.9版本時,原先的CUBIC擁塞控制算法就被替換爲Google的BBR算法了。
從下圖中能夠看到,當丟包率達到0.01%時,CUBIC就無法用了,而BBR並無問題,直到丟包率達到5%時BBR的帶寬才劇烈降低。
再來看資源的平衡分配。爲了公平的對待鏈接、用戶,服務器會作限速。好比下圖中的Leacky Bucket算法,它可以平滑突增的流量,更公平的分配帶寬。
再好比HTTP2中的優先級功能。一個頁面上有幾百個對象,這些對象的重要性不一樣,有些之間還互相依賴。好比,有些JS文件會包含jQuery.js,若是同等對待的話,即便先下載完前者,也沒法使用。
HTTP2容許瀏覽器下載對象時,根據解析規則,在stream中設置每個對象的weight優先級(255最大,0最小)。而各代理、資源服務器都會根據優先級,分配內存和帶寬,提高網絡效率。
最後看下TCP的報文效率,它也會影響HTTP性能。好比開啓Nagle算法後,網絡中的小報文數量大幅減小,考慮到40字節的報文頭部,信息佔比更高。
Cork算法與Nagle算法類似,但會更激進的控制小報文。Cork與Nagle是從發送端控制小報文,quickack則從接收端控制純ack小報文的數量,提升信息佔比。
說完相對微觀一些的信道,咱們再來從宏觀上看第三個優化點:傳輸路徑的優化。
傳輸路徑的第一個優化點是緩存,瀏覽器、CDN、負載均衡等組件中,緩存無處不在。
緩存的基本用法你大概很熟悉了,這裏咱們講過時緩存的用法。把過時緩存直接丟掉是很浪費的,由於「過時」是客戶端的定時器決定的,並不表明資源真正失效。
因此,能夠把它的標識符帶給源服務器,服務器會判斷緩存是否仍然有效,若是有效,直接返回304和空body就能夠了,很是節省帶寬。
對於負載均衡而言,過時緩存還可以保護源服務器,限制回源請求。當源服務器掛掉後,還能以過時緩存給用戶帶來降級後的服務體驗,這比返回503要好得多。
傳輸路徑的第二個優化點是慢啓動。系統自帶的TCP協議棧,爲了不瓶頸路由器丟包,會緩緩加大傳輸速度。它的起始速度就叫作初始擁塞窗口。
早期的初始擁塞窗口是1個MSS(一般是576字節),後來改到3個MSS(Linux 2.5.32),在Google的建議下又改到10個MSS(Linux 3.0)。
之因此要不斷提高起始窗口,是由於隨着互聯網的發展,網頁愈來愈豐富,體積也愈來愈大。起始窗口過小,就須要更長的時間下載第一個網頁,體驗不好。
固然,修改起始窗口很簡單,下圖中是Linux下調整窗口的方法。
修改起始窗口是常見的性能優化手段,好比CDN廠商都改過起始窗口,下圖是主流CDN廠商2014和2017年的起始窗口大小。
可見,有些窗口14年調得太大了,17年又縮回去了。因此,起始窗口並非越大越好,它會增長瓶頸路由器的壓力。
再來看傳輸路徑上,如何從拉模式升級到推模式。
好比 index.html 文件中包含<LINK href=」some.css」>,在HTTP/1中,必須先下載完index.html,才能去下載some.css,這是兩個RTT的時間。但在HTTP/2中,服務器能夠經過2個stream,同時並行傳送index.html和some.css,節約了一半的時間。
其實當出現丟包時,HTTP2的stream並行發送會嚴重退化,由於TCP的隊頭阻塞問題沒有解決。
上圖中的SPDY與HTTP2是等價的。在紅綠色這3個stream併發傳輸時,TCP層仍然會串行化,假設紅色的stream在最早發送的,若是紅色報文丟失,那麼即便接收端已經收到了完整的藍、綠stream,TCP也不會把它交給HTTP2,由於TCP自身必須保證報文有序。這樣併發就沒有保證了,這就是隊頭阻塞問題。
解決隊頭阻塞的辦法就是繞開TCP,使用UDP協議實現HTTP,好比Google的GQUIC協議就是這麼作的,B站在幾年前就使用它提供服務了。
UDP協議自身是不能保證可靠傳輸的,因此GQUIC須要從新在UDP之上實現TCP曾經作過的事。這是HTTP的發展方向,因此目前HTTP3就基於GQUIC在制定標準。
最後,再從網絡信息安全的角度,談談如何作優化。它實際上與編碼、信道、傳輸路徑都有關聯,但其實又是獨立的環節,因此放在最後討論。
互聯網世界的信息安全,始於1995年的SSL3.0。到如今,許多大型網站都更新到2018年推出的TLS1.3了。
TLS1.2有什麼問題呢?最大問題就是,它支持古老的密鑰協商協議,這些協議如今已經不安全了。好比2015年出現的FREAK中間人攻擊,就能夠用Amazon上的虛擬機,分分鐘攻陷支持老算法的服務器。
TLS1.3針對這一狀況,取消了在當前的計算力下,數學上已經再也不安全的非對稱密鑰協商算法。在Openssl的最新實現中,僅支持5種安全套件:
TLS1.3的另外一個優點是握手速度。在TLS1.2中,因爲須要2個RTT才能協商完密鑰,才誕生了session cache和session ticket這兩個工具,它們都把協商密鑰的握手下降爲1個RTT。可是,這兩種方式都沒法應對重放攻擊。
而TLS1.2中的安全套件協商、ECDHE公鑰交換這兩步,在TLS1.3中被合併成一步,這大大提高了握手速度。
若是你還在使用TLS1.2,儘快升級到1.3吧,除了安全性,還有性能上的收益。
HTTP的性能優化手段衆多,從這四個維度出發,能夠創建起樹狀的知識體系,囊括絕大部分的HTTP優化點。
編碼效率優化包括http header和body ,它可使傳輸的數據更短小緊湊,從而得到更低的時延和更高的併發。同時,好的編碼算法也能夠減小編解碼時的CPU消耗。
信道利用率的優化,能夠從多路複用、錯誤發現及恢復、資源分配這3個角度出發,讓快速的底層信道,有效的承載慢速的應用層信道。
傳輸路徑的優化,包括各級緩存、慢啓動、消息傳送模式等,它可以讓消息更及時的發給瀏覽器,提高用戶體驗。
當下互聯網中的信息安全,主要仍是創建在TLS協議之上的。TLS1.3從安全性、性能上都有很大的提高,咱們應當及時的升級。
但願這些知識可以幫助你全面、高效地優化HTTP協議!
騰訊雲最具價值專家,簡稱TVP(Tencent Cloud Valuable Professional),是騰訊雲頒發給技術專家的一項榮譽認證,以此感謝他們爲推進雲計算的發展所做出的貢獻。這些技術專家來自於各個技術領域和行業,他們熱衷實踐、樂於分享,爲技術社區的建設和推進雲計算的傳播作出了卓越的貢獻。
瞭解TVP更多信息,請關注「雲加社區」,回覆「TVP」。