http1.1與http2.0

簡介

  • http1.0: 1.0版本中每個TCP連接只能發送一個請求,數據發送完畢連接就關閉,如果還要請求其他資源,就必須重新建立TCP連接。(TCP為了保證正確性和可靠性需要客戶端和伺服器三次握手和四次揮手,因此建立連接成本很高)

  • http1.1:

    • 長連接:新增Connection欄位,默認為keep-alive,保持連接不斷開,即 TCP 連接默認不關閉,可以被多個請求復用;
    • 管道化:在同一個TCP連接中,客戶端可以發送多個請求,但響應的順序還是按照請求的順序返回,在服務端只有處理完一個回應,才會進行下一個回應;
    • host欄位:Host欄位用來指定伺服器的域名,這樣就可以將多種請求發往同一台伺服器上的不同網站,提高了機器的復用,這個也是重要的優化;
  • http2.0:

    • 二進位格式:1.x是文本協議,然而2.0是以二進位幀為基本單位,可以說是一個二進位協議,將所有傳輸的資訊分割為消息和幀,並採用二進位格式的編碼,一幀中包含數據和標識符,使得網路傳輸變得高效而靈活;
    • 多路復用:2.0版本的多路復用多個請求共用一個連接,多個請求可以同時在一個TCP連接上並發,主要藉助於二進位幀中的標識進行區分實現鏈路的復用;
    • 頭部壓縮:2.0版本使用使用HPACK演算法對頭部header數據進行壓縮,從而減少請求的大小提高效率,這個非常好理解,之前每次發送都要帶相同的header,顯得很冗餘,2.0版本對頭部資訊進行增量更新有效減少了頭部數據的傳輸;
    • 服務端推送:在2.0版本允許伺服器主動向客戶端發送資源,這樣在客戶端可以起到加速的作用;

性能比較

官方demo://http2.akamai.com/demo

自測demo:http1.1://47.95.8.93:441 , http2.0: //47.95.8.93:442

pingdom 測試結果:

Tokyo-load-time(s) London-load-time(s) Page-size(KB) Requests
http1.1 4.16~4.75 14.79~17.54 779.6 257
http2.0 3.79~4.33 5.78~6.54 745.5 257

本地demo:http1.1: //localhost:441, http2.0: //localhost:442(參見:http1-vs-http2

什麼場景下http2.0性能提升明顯?

時延(ms) 頻寬(Mbit/s) load-time(s) Requests
http1.1 100 4.65 257
http2.0 100 0.79 257
http1.1 1 7.17 257
http2.0 1 8.77 257
http1.1 100 1 7.19 257
http2.0 100 1 10.10 257
http1.1 100 5 4.75 257
http2.0 100 5 2.29 257
http1.1 200 5 9.14 257
http2.0 200 5 2.48 257

可以看到http2.0針對一些網站並非像官方提供的demo中提升巨大,這是因為不同的網路環境有不同的結果,這裡先給出結論:

結論:http2.0針對網路延時優化更好,但同時網路頻寬也需要相對較大,如果網路頻寬足夠,請求數量大,請求靜態資源多,http2.0相對於http1.1提升明顯

提升原因

影響網路性能的關鍵因素:「頻寬」 和 「延時

Mike Belshe關於網路性能及其影響因素做了測試,可以看到:按照目前網路發展速度,頻寬提升帶來的收益遠遠小於延時優化帶來的收益,每20毫秒的延遲提高,頁面載入時間就會近似線性提高!Mike Belshe的這項研究是Google開發SPDY協議的起點,該協議後來成為HTTP/2協議的基礎。

事實證明,對於大多數Web應用程式來說,頻寬不是限制性能的因素。相反,瓶頸是客戶端和伺服器之間的網路往返延遲。

二進位分幀層:— http2.0的基石

在二進位分幀層上,http2.0會將所有傳輸資訊分割為更小的消息和幀,並對它們採用二進位格式的編碼將其封裝,新增的二進位分幀層同時也能夠保證http1.X的各種動詞,方法,首部都不受影響,兼容上一代http標準。其中,http1.X中的首部資訊header封裝到Headers幀中,而request body將被封裝到Data幀中。

HTTP/2中最小的通訊單元,每個單元都包含一個幀頭,該幀頭至少標識幀所屬的流。

  • 所有通訊都通過單個TCP連接進行,該連接可以承載任意數量的雙向流。
  • 每個流都有一個唯一的標識符和可選的優先順序資訊,用於傳輸雙向消息。
  • 每條消息都是邏輯HTTP消息,如請求或響應,由一個或多個幀組成。
  • 幀是承載特定類型數據的最小通訊單元——例如HTTP標頭、消息有效負載等。來自不同流的幀可以交錯,然後通過每個幀標題中的嵌入式流標識符重新組裝。

多路復用:—連接共享

使用HTTP/1.x,如果客戶端想提出多個並行請求來提高性能,則必須使用多個TCP連接。這是HTTP/1.x框架的直接後果,該模型確保每個連接一次只能交付一個響應(響應排隊)。更糟糕的是,這也會導致線頭阻塞和底層TCP連接的使用效率低下。

HTTP/2中新的二進位幀層允許客戶端和伺服器將HTTP消息分解為獨立幀,將它們交織在一起,然後在另一端重新組裝它們,從而實現完整的請求和響應復用,這是HTTP/2最重要的增強功能。

TTFB:表示客戶端從發出請求後到收到伺服器響應的第一個位元組的時間,即包含了一個c/s的網路來回時間和伺服器處理請求的時間

可以看到http1.1在一個TCP連接中只有等一個請求處理完成之後才能處理下一個請求,而http2.0依賴於二進位分幀層和多路復用技術可以同時並行處理請求,減少了http1.1中的阻塞問題,這也是http2.0對網路時延優化好的重要原因


http1.1協議中請求一個服務端一般允許開放6個tcp連接,而http2.0隻有1個tcp連接,雖然節省了資源,但是在比較特殊的場景:小時延(忽略不計),小頻寬的場景中,http1.1相當於6個管道下載資源,http2.0則是一個管道,此時http2.0的載入速度反而不如http1.1;

當然這種場景如今已經不多見了,隨著互聯網的高速發展,頻寬已經不是影響響應的主要因素,大部分的情況下,延遲是影響響應速度的主要因素。

流優先順序

把http消息分為很多獨立幀之後,就可以通過優化這些幀的交錯和傳輸順序進一步優化性能,HTTP/2標準允許每個流具有相關的權重和依賴性:分配處理資源和客戶端與伺服器間的頻寬,不同優先順序的混合也是必須的。客戶端會指定哪個流是最重要的,有一些依賴參數,這樣一個流可以依賴另外一個流。優先順序別可以在運行時動態改變,當用戶滾動頁面時,可以告訴瀏覽器哪個影像是最重要的,你也可以在一組流中進行優先篩選,能夠突然抓住重點流(偏向但並不是絕對的優先)。

  • 優先順序最高:主要的html
  • 優先順序高:CSS文件
  • 優先順序中:js文件
  • 優先順序低:圖片

每個伺服器源一個鏈接

受益於的二進位框架,HTTP/2不再需要使用多個TCP並行連接;每個流被拆分為多個幀,可以並行、交錯和優先排序。所有HTTP/2連接都是持久的,每個源只需要一個連接,通過重用相同的連接,HTTP/2既能更有效地利用每個TCP連接(TCP慢啟動),又能顯著減少整體協議開銷,有助於提高吞吐量和降低運營成本。

  • 減少連接數量是提高HTTPS部署性能的一個特別重要的功能:這意味著減少昂貴的TLS握手,更好的會話重用,以及全面減少所需的客戶端和伺服器資源。

  • 在特定場景中,多個TCP連接可能證明是有益的。然而,HTTP/2的實驗證據表明,單個連接是首選策略

頭部壓縮

每個HTTP傳輸都包含一組標頭,描述傳輸的資源及其屬性。在HTTP/1.x中,此元數據始終以純文本形式發送,每次傳輸添加500-800位元組的開銷,如果使用HTTP Cookie,有時會增加千位元組;HTTP/2使用HPACK壓縮演算法壓縮請求和響應頭元數據,HTTP/2的HPACK演算法使用一份索引表來定義常用的http Header,把常用的 http Header 存放在表裡,請求的時候便只需要發送在表裡的索引位置即可。

服務端推送

HTTP/2的另一個強大的新功能是伺服器能夠為單個客戶端請求發送多個響應。也就是說,除了響應原始請求外,伺服器還可以向客戶端推送其他資源,而無需客戶端請求每個資源!

升級難度

根據上述得到的結論:HTTP2.0主要針對網路時延的優化,在不考慮網路延時的情況下,HTTP1.1與HTTP2.0性能相差不大,甚至HTTP1.1性能更好。對於在服務端內部:是否要升級HTTP2.0,個人理解,服務端伺服器本身相近,甚至反向代理前後端服務部署在同一台伺服器,本身就沒有時延,升級帶來的優化並不會有大跨度提升;

註:【在互聯網架構中,web 伺服器:一般指像 nginx,apache 這類的伺服器,他們一般只能解析靜態資源。應用伺服器:一般指像 tomcat,jetty,resin 這類的伺服器可以解析動態資源也可以解析靜態資源,但解析靜態資源的能力沒有 web 伺服器好。一般只有 Web 伺服器才能被外網訪問,應用伺服器只能內網訪問。】

如果想嘗試在服務端內部使用HTTP2.0,也可以進行相關配置,HTTP2.0有兩種協議版本:H2C和H2,其中:

  • H2C是HTTP2.0的明文版本,不需要經過證書認證,按理說這是服務端進行內部調用的正確選擇,然而在Spring boot 2.0的官方文檔來看,它明確指出「Springboot不支援h2c「,如果想使用springboot支援h2c可以參考這篇文章

  • springboot雖然支援H2協議,但其有諸多限制:首先使用H2的條件是支援HTTPS,這就需要後端服務的調用進行證書認證,這就不盡合理;其次,大部分後端服務現在仍在使用Java1.8,但是在原生的JDK8中是不支援H2協議的,直到JDK9,Java自帶的HTTP client(HttpURLConnection)才支援H2,因此還需要選擇支援HTTP/2的應用伺服器:

    • Undertow 1.4.0+:如果您使用Java 8,則無需額外的配置來支援HTTP2。

    • Jetty 9.4.11:如果您使用Java 8,您還需要加密類庫,並需要依賴Jetty的以下兩個模組:

      org.eclipse.jetty:jetty-alpn-conscrypt-server
      org.eclipse.jetty.http2:http2-server
      
    • Tomcat 9.0.x:如果您使用Java 8,您還需要單獨下載libtcnative類庫(使用APR連接器並將升級協議設置為Http2Protocol),並在啟動Tomcat時添加JVM啟動參數,如下所示:

      -Djava.library.path=/usr/local/opt/tomcat-native/lib
      
    • Tomcat 9.0.x:如果您使用Java 9,則不需要額外的配置來支援HTTP2。然而springboot2默認的tomcat是8.5+,

    具體的詳細配置請參見這邊文章

  • 目前項目中常用的微服務之間的調用工具Openfeign、restTemplate工具底層都使用了HTTP client,默認都是JavaJDK中的HttpURLConnection,但可以進行替換第三方的HTTP Client使用,常見的HTTP client:

    • 基於HttpURLConnection的SimpleBufferingClientHttpRequest和SimpleStreamingClientHttpRequest,不支援HTTP2
    • 基於OkHttpClient的OkHttp3ClientHttpRequest,支援HTTP2
    • 基於Apache HttpComponents HttpClient,不支援HTTP2
    • 基於Netty HttpClient的ReactorClientHttpConnector(Netty4ClientHttpRequest已過時),支援HTTP2(還需要第三方APLN實現)

    spring5中新提供的webclient,默認底層使用Netty,內置支援Reactor反應性HttpClient實現,目前不支援HTTP2。

    同時,也可以通過編碼的方式實現ClientHttpConnector介面自定義新的底層庫;如切換Jetty實現

綜上所述:後端服務,服務服務之間沒有必要使用HTTP2.0協議,整體來看,整體環境支援體系並不完善,且在小時延場景下,性能並不會有大幅提升,更重要的是HTTP2.0是基於HTTPS的協議,在後端服務之間使用並不合適,如果優化不當,甚至適得其反;

應用HTTP2.0

如果你使用 SSL/TLS(簡稱 TLS),那麼 HTTP/2 可以提升網站性能。如果你沒有,那在使用 HTTP/2 之前要先支援 TLS。這時候,使用 TLS 的性能損耗大致可以被使用 HTTP/2 的性能提升抵銷。不過還是建議你在實際應用之前先測試一下。

事實上,部署 HTTP/2 並不難。如果使用 NGINX,只要在配置文件中啟動相應的協議就可以了。瀏覽器和伺服器會協商採用什麼協議,如果瀏覽器支援 HTTP/2(而且也在使用 TLS),就會使用 HTTP/2。

HTTP/2 要求 Nginx 1.9.5+,,OpenSSL 1.0.2+

nginx配置http2.0:

server {
    listen 442 ssl http2;

	#證書
    ssl_certificate /usr/local/etc/nginx/ssl-cert.pem;
    ssl_certificate_key /usr/local/etc/nginx/ssl-key.pem;

    add_header Cache-Control no-store; #瀏覽器載入不使用快取
    
    location / {
        root   /usr/local/share/nginx/html;
        index  index2.0.html;
    }
}

HTTP的應用程式語義相同,沒有對提供的功能或核心概念進行更改,如HTTP方法、狀態程式碼、URI和標頭欄位

性能瓶頸:啟用http2.0後會給性能帶來提升,但同時也會帶來新的性能瓶頸。因為現在所有的壓力集中在底層一個TCP連接之上,TCP很可能就是下一個性能瓶頸,比如TCP分組的隊首阻塞問題,單個TCP packet丟失導致整個連接阻塞,無法逃避,此時所有消息都會受到影響。未來,伺服器端針對http 2.0下的TCP配置優化至關重要。

前端改動:前端針對http1.1的優化可能不在需要

後端改動:後端不需要改動