HTTP
http是目前應用最廣泛的應用層協議,截止到目前為止已經發布了多個版本,最常用的是http1.1和http2。
http0.9是最早的版本,功能很簡單,沒有header,只支持GET。
http1.0
-
只支持短連接,即每次請求一個資源就會新建一次tcp連接,服務器寫完響應行后立刻將TCP連接關閉
由於tcp連接的建立和關閉需要經歷三次握手和四次握手,再加上tcp慢啟動特性,報文一開始不會滿負荷傳輸,所以不僅開銷很大,而且效率很低。
-
相比於上一版本,增加了http header,和status code用於聲明請求的結果,content-type字段也可以用來聲明傳輸其他文件,頭部還增加了協議的版本號。
http1.1
http1.1是1997年發布的,是現在使用最廣泛的版本,這個版本引入了長連接,減少了tcp連接建立和關閉的消耗。數據傳輸完成了保持TCP連接不斷開(不發RST包、不四次握手),等待在同域名下繼續用這個通道傳輸數據。
連接的本質是雙方各自初始化了一個socket的標識符,可以當成一個文件,以后這個連接發送過來的東西都存到這個里面,所以說連接只是讓雙方確認一下,認識到以后有數據進來可以識別出來。發送請求其實就是先把要發送的數據存到一個緩沖區,然后等待操作系統幫你一層層打包發送到網卡然后走網線出去而已,接受就是一個相反的過程本質是一樣的。
長連接模式下,連接關閉的主動權在客戶端這里,當客戶端收到服務器傳來的響應時,如果不需要再次發送請求,那么它會發送一個connection:close的數據包來表示關閉連接。而為了避免TCP連接一直占用着浪費資源,服務器端也維護着一個keepalive_timeout參數,如果長時間沒收到來自客戶端的請求,服務器端也會主動關閉釋放連接。
-
新加了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 25\r\n This is the data in the first chunk\r\n 0\r\n \r\n
其他的改變還有增加了Host頭,讓服務端知道用戶請求的是哪個域名等。
2014年,更新了內容,增加了TLS支持,即https傳輸,除了短連接和長連接模型,還支持服務端push模型,websocket模型。
補充問題1:k8s中長連接watch機制如何實現的?
http最大的特點就是它是一個拉協議,http無法做到服務端向客戶端主動推送數據。但是由於http協議應用廣泛,很多時候又想使用http協議去實現實時數據的獲取,怎么辦呢?
-
輪詢
輪詢是最普遍的基於HTTP協議采用拉的方式獲取實時數據的,輪詢又分為短輪詢和長輪詢。
長輪詢是服務器收到請求后,如果有數據會立刻響應請求。如果沒有數據,就會把請求掛起一段時間,等待有沒有數據產生,如果時間到了還沒有數據,再響應http請求。長輪詢的特點是實時性高,當然缺點就是會造成資源浪費。
短輪詢就是客戶端按照一定時間間隔去請求http服務器,服務器會立即做出響應,不管有沒有可用的數據。優點就是短連接,服務器處理方便。缺點是實時性低,很多無效請求,性能開銷大。
-
http chunked
前面已經介紹過分塊傳輸編碼了,使用分塊傳輸編碼,數據分解成一系列數據塊,並以一個或多個塊發送,這樣服務器可以發送數據而不需要預先知道發送內容的總大小。只要瀏覽器沒有遇到結束標識,就會邊解析邊執行對應的響應內容。k8s的watch機制就是基於分塊傳輸編碼,當有新的數據產生時,發送一個數據塊,而客戶端在沒遇到大小為0的數據塊之前,會一直等待下一個數據塊的到來。
補充問題2:什么情況下,客戶端發送的http請求會復用同一個TCP連接?
-
請求的是不同域名,肯定不復用
-
請求的是相同域名,如果多個請求是並行的,就不復用。如果請求是一個一個完成的,那么就會復用。當然前提是http1.1 keep-alive情況。
HTTP/1.1 存在一個問題,單個 TCP 連接在同一時刻只能處理一個請求,意思是說:兩個請求的生命周期不能重疊,任意兩個 HTTP 請求從開始到結束的時間在同一個 TCP 連接里不能重疊。在 HTTP/1.1 存在 Pipelining 技術可以完成這個多個請求同時發送,但是由於瀏覽器默認關閉,所以可以認為這是不可行的。在 HTTP2 中由於 Multiplexing 特點的存在,多個 HTTP 請求可以在同一個 TCP 連接中並行進行。
http2
http2是2015年發布的,主要就是提升安全性與性能,gRPC底層就是使用的http2協議。
-
多路復用(Multiplexing):就是說 HTTP/2 可以重復使用同一個 TCP 連接,並且連接是多路的,多個請求或響應可以同時傳輸,這也是gRPC支持客戶端流和服務器端流的原因
-
對比之下,HTTP/1.1 的長連接也能復用 TCP 連接,但是只能串行,不能“多路”。
-
Rest API通常構建在http1.1上,是請求響應模式。如果一個微服務收到多個客戶端請求,每次只能處理一個,即使構建在http2上,依舊是請求響應模式,無法發揮http2的優勢:多路復用。
gRPC構建在http2上,支持流式通信和雙向通信,通過不斷流式傳輸信息同時處理這些請求。但雙向流模式依舊需要客戶端發起調用。這里的流式通信,不是全雙工的,只是將原本的請求響應風格,變為了同時可以發送多個請求,服務器也能發送多個響應。
-
-
二進制協議:HTTP/2 的消息頭使用二進制格式,而非文本格式。並且使用專門設計的 HPack 算法壓縮。
-
服務器推送:服務端能夠直接把資源推送給客戶端,當客戶端需要這些文件的時候,它已經在客戶端了。(HTTP/2推送服務器只能被瀏覽器來處理,而不是應用)
-
通訊雙方cache一份header filed表,用來差量更新頭部字段,避免重復header的傳輸
HTTP3
2018年發布,基於谷歌的QUIC,底層使用udp代碼tcp協議,
這樣解決了隊頭阻塞問題,同樣無需握手,性能大大地提升,默認使用tls加密。
WebSocket
websocket是一個雙向通信協議,它在握手階段采用http1.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 // 表明選擇的子協議
-
服務器如果支持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的能力相結合可以在多數場景下達到同樣的效果,但是會更簡單。