HTTP2和WebSocket

HTTP

http是目前應用最廣泛的應用層協議,截止到目前為止已經發布了多個版本,最常用的是http1.1和http2。

http0.9是最早的版本,功能很簡單,沒有header,只支援GET。

http1.0

  • 只支援短連接,即每次請求一個資源就會新建一次tcp連接,伺服器寫完響應行後立刻將TCP連接關閉

    由於tcp連接的建立和關閉需要經歷三次握手和四次握手,再加上tcp慢啟動特性,報文一開始不會滿負荷傳輸,所以不僅開銷很大,而且效率很低。

  • 相比於上一版本,增加了http header,和status code用於聲明請求的結果,content-type欄位也可以用來聲明傳輸其他文件,頭部還增加了協議的版本號。

http1.1是1997年發布的,是現在使用最廣泛的版本,這個版本引入了長連接,減少了tcp連接建立和關閉的消耗。

  • 新加了connection請求頭欄位,false表示短連接,keep-alive表示長連接(默認)

    在持久連接模式中,由於伺服器不會立刻關閉TCP連接,所以需要在響應中加上一個Content-Length的響應頭來表示響應體的長度,讓瀏覽器判斷HTTP響應是否結束。如果沒有這個響應頭的話,瀏覽器會處於pending的狀態,因為在它看來還有數據要接收。

    所以長連接無非就是通過Content-Length欄位把連接關閉的主動權交給了客戶端。

  • 支援分塊傳輸編碼,可以發送動態數據,只在1.1版本中可以使用

    使用分塊傳輸編碼,數據分解成一系列數據塊,並以一個或多個塊發送,這樣伺服器可以發送數據而不需要預先知道發送內容的總大小,使得伺服器可以發送動態生成的數據。

    設置相應頭Transfer-Encoding:chunked,此時消息體便由數量未定的塊組成,並以最後一個大小為0的塊結束。

    HTTP/1.1 200 OK\r\n
    \r\n
    Transfer-Encoding: chunked\r\n
    ...\r\n
    \r\n
    <chunked 1 length>\r\n
    <chunked 1 content>\r\n
    <chunked 2 length>\r\n
    <chunked 2 content>\r\n
    ...\r\n
    0\r\n
    \r\n
    \r\n
    

其他的改變還有增加了Host頭,讓服務端知道用戶請求的是哪個域名等。

2014年,更新了內容,增加了TLS支援,即https傳輸,除了短連接和長連接模型,還支援服務端push模型,websocket模型。

Http2是2015年發布的,主要就是提升安全性與性能,gRPC底層就是使用的http2協議。

  • 多路復用(Multiplexing):就是說 HTTP/2 可以重複使用同一個 TCP 連接,並且連接是多路的,多個請求或響應可以同時傳輸,這也是gRPC支援客戶端流和伺服器端流的原因

    img

    • 對比之下,HTTP/1.1 的長連接也能復用 TCP 連接,但是只能串列,不能「多路」。

    • Rest API通常構建在http1.1上,是請求響應模式。如果一個微服務收到多個客戶端請求,每次只能處理一個,即使構建在http2上,依舊是請求響應模式,無法發揮http2的優勢:多路復用。

      gRPC構建在http2上,支援流式通訊和雙向通訊,通過不斷流式傳輸資訊同時處理這些請求。但雙向流模式依舊需要客戶端發起調用。這裡的流式通訊,不是全雙工的,只是將原本的請求響應風格,變為了同時可以發送多個請求,伺服器也能發送多個響應。

  • 二進位協議:HTTP/2 的消息頭使用二進位格式,而非文本格式。並且使用專門設計的 HPack 演算法壓縮。

  • 伺服器推送:服務端能夠直接把資源推送給客戶端,當客戶端需要這些文件的時候,它已經在客戶端了。(HTTP/2推送伺服器只能被瀏覽器來處理,而不是應用)

  • 通訊雙方cache一份header filed表,用來差量更新頭部欄位,避免重複header的傳輸

HTTP3

2018年發布,基於Google的QUIC,底層使用udp程式碼tcp協議,

這樣解決了隊頭阻塞問題,同樣無需握手,性能大大地提升,默認使用tls加密。

WebSocket

websocket是一個雙向通訊協議,它在握手階段採用http1.1

握手過程

  1. 發起握手請求

    HTTP/1.1 101 Switching Protocols  // 狀態行
    Upgrade: websocket   // required
    Connection: Upgrade  // required
    Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo= // required,加密後的 Sec-WebSocket-Key
    Sec-WebSocket-Protocol: chat // 表明選擇的子協議
    
  2. 伺服器如果支援websocket,返回101響應

    HTTP/1.1 101 Switching Protocols  // 狀態行
    Upgrade: websocket   // required
    Connection: Upgrade  // required
    Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo= // required,加密後的 Sec-WebSocket-Key
    Sec-WebSocket-Protocol: chat // 表明選擇的子協議
    

WebSocket 提供兩種協議:不加密的 ws:// 和 加密的 wss://. 因為是用 HTTP 握手,它和 HTTP 使用同樣的埠:ws 是 80(HTTP),wss 是 443(HTTPS)

HTTP2 vs WebSocket

WebSocket是全雙工的,可以雙向通訊,主要應用在實時通訊的場景中,伺服器可以實時推送數據給客戶端。

HTTP/2 雖然也支援 Server Push,但是伺服器只能主動將資源推送到客戶端快取!那不是應用程式可以感知的,主要是讓瀏覽器(用戶代理)提前快取靜態資源。

在典型的HTTP 1.x工作流中,瀏覽器請求一個頁面,伺服器在響應中返回一個HTML,然後就時等待瀏覽器解析響應並發送額外請求來獲取額外的內嵌資源(JavaScript、CSS等)。伺服器推送使伺服器能夠試探性地向客戶端發送資源。此時,瀏覽器不必解析HTML頁面並找到需要載入的其它資源;而是伺服器能夠立即開始發送它們。

但是人類的想像力是非常豐富的。比如為了讓伺服器可以推送消息給客戶端,使用了一個「偏方」。它就是SSE(Server Sent Event)。SSE是一種讓伺服器能夠在客戶端伺服器建立連接之後非同步推送數據給客戶端的機制。由於SSE是基於HTTP的,其天然適配於HTTP/2,這樣SSE就可以集兩者之長:HTTP/2可以基於多路復用流形成一個高效傳輸層,同時SSE給應用提供了API使之能夠進行推送。

Websocket技術可能會繼續使用,但是SSE和其EventSource API同HTTP/2的能力相結合可以在多數場景下達到同樣的效果,但是會更簡單。