一、HTTP1.x存在的問題
Http1.0時Connection無法復用,同一時間一個Connection只能處理一個request。Http1.1引入了Request pipelining來解決這一問題,Request pipelining。
Requestpipeling在FIFO基礎上支持同一Connection並發處理多個Request,這里的FIFO是指Http Response發送順序必須與Request的發送順序保持一致。
詳情前往https://en.wikipedia.org/wiki/HTTP_pipelining。
然而它並不完美,仍然有HOL Blocking問題。
所謂的行首阻塞是因於FIFO的原因,導致后面的Reponse由於其之前的Response由於資源搶占等原因無法輸出而Block。
由於這樣的限制,HTTP/1.0及HTTP/1.1時代需要對Server端創建多個連接,通過提高並發度來降低Latency。
另外Http2支持Header壓縮,而Header壓縮在此前是不支持的,此前一般是對body進行gzip壓縮。
二、HTTP2的解決方案
HTTP2在協議上真正要求不同的Request可以在同一個Connection上交錯進行,真正做到多路復用。所帶來的好處顯而易見,更少的Connection,更好的並發,更高效的網絡資源利用。
支持流量控制及請求優先級,使得重要的請求優先得到處理,這一點對於應用來說是個大的優化點,以體驗為目標,對不同的請求划分優先級以及流量控制,比如異步加載的內容重要性低,可以設置較低優先級。但是個人覺得這一點要做好很難,要依賴於瀏覽器,應用服務器,應用程序三個地方都有非常好的實現,是否是合理的實現還需要好的度量平台,視效果而定。
Server Push機制支持Server端應用程序可以預先輸出內容暫未需要的內容,從而降低潛在的延時。舉例:可以把css內容與html內容同步輸出,而不需要等待html完全輸出后,瀏覽器再加載css。
支持對Http Header進行壓縮。
高效的二進制格式(相對文本格式)傳輸。
三、原理分析
將通過Jetty源碼來分析Jetty如何支持Http2的
Jetty源碼地址:git://git.eclipse.org/gitroot/jetty/org.eclipse.jetty.project.git
A. Jetty.io&Jetty.server主要類結構圖:
Jetty.io主要是對EndPoint及Connection的定義和基本實現,Jetty.server實現了絡的交互過程,包裝了Socket,Channel,多路復用等實現。這兩個包一起對應用程序端或協議格式端提供了網絡交互過程,從而實現網絡交互過程對於協議及應用的透明。
B. 與Http2協議的結合:
Jetty Basic如上圖已經介紹,HTTP2的實現只擴展ServerConnection,ServerConnectionFactory即可,相當於Jetty內核上增加插件,擴展性好,另外也不需要關注網絡細節
C. 對重點類的重點解讀:
類名 | 職責 | 詳細介紹 |
---|---|---|
HTTP2Flusher | Frame輸出控制類 | 重點說明: HTTP2Flusher類中保存了各種Entry隊列,Entry中包含Frame數據,其對應的Stream引用,以及對應的CallBack。 主要屬性: Queue<WindowEntry> windows,WindowUpdateFrame類型的保存隊列 ArrayQueue<Entry> frames,Frame數據隊列 主要方法: window(), prepend(), append(), remove()等都是針對如上Queue的操作 process()真正執行Frame的輸出。首先執行Window隊列,將Window的窗口設置數據寫入到對應的Stream或Session中;然后執行WindowsSize的限流邏輯,若對應的Session或Stream當前WindowSize不大於0,則不發送Frame,否則將WindowSize減去當前Frame的Size,應用於下次限流。最后調用Session中的EndPoint類的write方法將Frame輸出 |
FlowControlStrategy | 流控接口定義 | 重點說明: 其實現類主要實現了依據發送或接收的Frame的數據length,依據固定策略改寫Session及Stream的WindowSize,以及Stream另一端EndPoint對於WindowSize的要求,完成輸科和輸出限流的完整邏輯 |
Http2Session | ISession的實現類 | 主要屬性: 1. EndPoint endPoint, 表征了此Session的網絡端點,對於網絡的操作通過調用endPoint的方法實現 2. Generator generator,用於按需生成各種格式的Frame,Frame被最終寫入endPoint中 3. Listener listener,Session中被動接口的邏輯實現,面向接口編程,支持多種實現 4. FlowControlStrategy flowControl前面已經講述 5. HTTP2Flusher flusher前面也已講述 主要方法: 1.newStream(),創建新的流對象,以及HeaderFrame,將流寫入Session的流緩存中,流緩存通過ConcurrentHashMap實現;並將流和HeaderFrame追加到Http2Flusher對象的ArrayQueue中; 2. push(), settings(), ping(), reset()的實現雷同,最終都是將對應格式的Frame放入Http2Flusher的ArrayQueue中。Http2Flusher是真正輸出Frame的控制類。 3. onData(),接收DataFrame時的處理方法,首先更新當前FlowControl對象中的WindowSize,並執行Window限流邏輯,若未超出限流控制,則調用Stream的處理方法處理接收到的數據,否則直接丟棄當前Frame,最后將WindowSize恢復之前值 4. onHeaders()接收HeaderFrame時的處理方法,抽象方法,交由子類實現 5. onPriority(),Jetty默認未支持客戶端發起的對優先級的支持 6. onReset(), onSettings(),onPing(),onGoAway(),onWindowUpdate(),onConnectionFailure()執行對應邏輯,通過Listener接口,支持對於這些請求數據的自定義實現 |
HTTP2Stream | IStream的實現類 | 重點說明: 實現了IStream的主動接口及其被動接口Stream.Listener。其主動接口一般是通過調用對應的ISession接口實現 |
HTTP2ServerSession | 繼承HTTP2Session,實現Server端Session特有的邏輯 | 重點說明: 特有邏輯包括ServerSession在接收onPreface時,需回復一個Settings Frame;在接收Headers請求時需創建RemoteStream對象;當接收ServerPush時,報出異常,HTTP2中Client不能向Server發送Server Push。 |
DataFrame | HTTP2協議的各種數據格式的封閉 | 重點說明: 針對不同的數據格式,如ServerPush,Preface等,有對應的子類實現 |
DataGenerator | 構建如上的DataFrame | 重點說明: 對於不同的數據格式有不同的子類實現 |
parser包 | 對接收的數據格式進行解析 | 重點說明: 對接收的數據格式進行解析 |
四、如何實現HTTP2
HTTP2還未被所有瀏覽器所支持,因此在實施時要支持多種協議並存.
需要Http Server端支持HTTP2協議,據我所知Tengine尚未支持,已支持的Server列表https://github.com/http2/http2-spec/wiki/Implementations。
若要充分利用HTTP2的所有優點,需要在應用程序端(甚至是JAVA)的輸出行為作智能處理,如通過大數據來分析哪些資源適合用Server Push並行輸出,哪些資源優先級可以隱藏低,如何在性能與復雜度及維護成本之間做出平衡,需要業內更多的人努力和嘗試,