HTTP2 Server Push的研究

發表於2017-01-05

1,HTTP2的新特性。

關於HTTP2的新特性,讀著可以參看我之前的文章,這裡就不在多說了,本篇文章主要講一下server push這個特性。

HTTP,HTTP2.0,SPDY,HTTPS你應該知道的一些事

 

2,Server Push是什麼。

簡單來講就是當使用者的瀏覽器和伺服器在建立連結後,伺服器主動將一些資源推送給瀏覽器並快取起來,這樣當瀏覽器接下來請求這些資源時就直接從快取中讀取,不會在從伺服器上拉了,提升了速率。舉一個例子就是:

假如一個頁面有3個資原始檔index.html,index.css,index.js,當瀏覽器請求index.html的時候,伺服器不僅返回index.html的內容,同時將index.css和index.js的內容push給瀏覽器,當瀏覽器下次請求這2兩個檔案時就可以直接從快取中讀取了。

3,Server Push原理是什麼。

要想了解server push原理,首先要理解一些概念。我們知道HTTP2傳輸的格式並不像HTTP1使用文字來傳輸,而是啟用了二進位制幀(Frames)格式來傳輸,和server push相關的幀主要分成這幾種型別:

  1. HEADERS frame(請求返回頭幀):這種幀主要攜帶的http請求頭資訊,和HTTP1的header類似。
  2. DATA frames(資料幀) :這種幀存放真正的資料content,用來傳輸。
  3. PUSH_PROMISE frame(推送幀):這種幀是由server端傳送給client的幀,用來表示server push的幀,這種幀是實現server push的主要幀型別。
  4. RST_STREAM(取消推送幀):這種幀表示請求關閉幀,簡單講就是當client不想接受某些資源或者接受timeout時會向傳送方傳送此幀,和PUSH_PROMISE frame一起使用時表示拒絕或者關閉server push。

Note:HTTP2.0相關的幀其實包括10種幀,正是因為底層資料格式的改變,才為HTTP2.0帶來許多的特性,幀的引入不僅有利於壓縮資料,也有利於資料的安全性和可靠傳輸性。

瞭解了相關的幀型別,下面就是具體server push的實現過程了:

  1. 由多路複用我們可以知道HTTP2中對於同一個域名的請求會使用一條tcp連結而用不同的stream ID來區分各自的請求。
  2. 當client使用stream 1請求index.html時,server正常處理index.html的請求,並可以得知index.html頁面還將要會請求index.css和index.js。
  3. server使用stream 1傳送PUSH_PROMISE frame給client告訴client我這邊可以使用stream 2來推送index.js和stream 3來推送index.css資源。
  4. server使用stream 1正常的傳送HEADERS frame和DATA frames將index.html的內容返回給client。
  5. client接收到PUSH_PROMISE frame得知stream 2和stream 3來接收推送資源。
  6. server拿到index.css和index.js便會傳送HEADERS frame和DATA frames將資源傳送給client。
  7. client拿到push的資源後會快取起來當請求這個資源時會從直接從從快取中讀取。

下圖表示了整個流程:

%e5%b1%8f%e5%b9%95%e5%bf%ab%e7%85%a7-2016-11-27-19-07-58

4,Server Push怎麼用。

既然server push這麼神奇,那麼我們如何使用呢?怎麼設定伺服器push哪些檔案呢?

首先並不是所有的伺服器都支援server push,nginx目前還不支援這個特性,可以在nginx的官方部落格上得到證實https://www.nginx.com/blog/http2-r7/,但是Apache和nodejs都已經支援了server push這一個特性,需要說明一點的是server push這個特性是基於瀏覽器和伺服器的,所以瀏覽器並沒有提供相應的js api來讓使用者直接操作和控制push的內容,所以只能是通過header資訊和server的配置來實現具體的push內容,本文主要以nodejs來說明具體如何使用server push這一特性。

準備工作:下載nodejs http2支援,本地啟動nodejs服務。

1. 首先我們使用nodejs搭建基本的server:

這幾行程式碼就是簡單搭建一個nodejs http2服務,開啟chrome,我們可以看到所有請求都走了http2,同時也可以驗證多路複用的特性。

h21

這裡需要注意幾點:

  1. 建立http2的nodejs服務必須時基於https的,因為現在主流的瀏覽器都要支援SSL/TLS的http2,證照和私鑰可以自己通過OPENSSL生成。
  2. node http2的相關api和正常的node httpserver相同,可以直接使用。

2. 設定我們的server push:

我們設定了bootstrap.min.css來通過server push到我們的瀏覽器,我們可以在瀏覽器中檢視:

h22

可以看到,啟動server push的資源timelime非常快,大大加速了css的獲取時間。

這裡需要注意下面幾點:

  1. 我們呼叫response.push(),就是相當於server發起了PUSH_PROMISE frame來告知瀏覽器bootstrap.min.css將會由server push來獲取。
  2. response.push()返回的物件時一個正常的ServerResponse,end(),writeHeader()等方法都可以正常呼叫。
  3. 這裡一旦針對某個資源呼叫response.push()即發起PUSH_PROMISE frame後,要做好容錯機制,因為瀏覽器在下次請求這個資源時會且只會等待這個server push回來的資源,這裡要做好超時和容錯即下面的程式碼:
  4. 上面的程式碼你可能會發現許多和正常nodejs的httpserver不一樣的東西,那就是stream,其實整個http2都是以stream為單位,這裡的stream其實可以理解成一個請求,更多的api可以參考:node-http2

  5. 最後給大家推薦一個老外寫的專門服務http2的node server有興趣的可以嘗試一下。https://gitlab.com/sebdeckers/http2server

5,Server Push相關問題。

  1. 我們知道現在我們web的資源一般都是放在CDN上的,那麼CDN的優勢和server push的優勢有何區別呢,到底是哪個比較快呢?這個問題筆者也一直在研究,本文的相關demo都只能算做一個演示,具體的線上實踐還在進行中。
  2. 由於HTTP2的一些新特性例如多路複用,server push等等都是基於同一個域名的,所以這可能會對我們之前對於HTTP1的一些優化措施例如(資源拆分域名,合併等等)不一定適用。
  3. server push不僅可以用作拉取靜態資源,我們的cgi請求即ajax請求同樣可以使用server push來傳送資料。
  4. 最完美的結果是CDN域名支援HTTP2,web server域名也同時支援HTTP2。

 

參考資料:

  1. HTTP2官方標準:https://tools.ietf.org/html/rfc7540
  2. 維基百科:https://en.wikipedia.org/wiki/HTTP/2_Server_Push
  3. https://www.nihaoshijie.com.cn/index.php/archives/651

相關文章