原文:Optimize Your App with HTTP/2 Server Push Using Node and Express
做者:Azat Mardan
代碼:http2-node-server-pushjavascript
HTTP/2
是 Web 開發的新標準,擁有不少不錯的優勢可以讓 Web 訪問更快且開發的工做更輕鬆簡單。好比,引入多路複用傳輸不用合併資源,服務器推送(Server Push)資源讓瀏覽器預加載。html
該文不會講述 HTTP/2
的全部優點。你能夠經過上篇文章瞭解更多{% post_link http2-node-express %}。該文主要關注於在 Node.js
環境使用 Express.js
和 HTTP/2
庫 spdy。java
服務器推送(Server Push)工做方式是經過在一個 HTTP/2
請求中捆綁多個資源。在底層,服務器會發送一個 PUSH_PROMISE
,客戶端(包括瀏覽器)就能夠利用它且不基於 HTML
文件是否須要該資源。若是瀏覽器檢測到須要該資源,就會匹配到收到的服務器推送的 PROMISE
而後讓該資源表現的就像正常的瀏覽器 Get
請求資源。顯而易見,若是匹配到有推送,瀏覽器就不須要從新請求,而後直接使用客戶端緩存。這推薦幾篇文章關於服務器推送(Server Push)的好處:node
這是個關於在 Node.js
實現服務器推送(Server Push)實踐教程。爲了更清晰精簡,咱們只實現一個路由地址 /pushy
的 Node.js
和 Express.js
服務器,它會推送一個 JS 文件,正如以前所說,咱們會用到一個 HTTP/2
庫 spdy。web
先解釋一下,爲啥在 Node.js
環境選擇 HTTP/2
庫 spdy。當前來講,爲 Node.js
主要有兩個庫實現了 HTTP/2
:chrome
兩個庫都跟 Node.js
核心模塊的 http
和 https
模塊 api 很類似。這就意味着若是你不使用 Express
,這兩個庫就沒什麼區別。然而, spdy
庫支持 HTTP/2
和 Express
,而 http2
庫當前不支持 Express
。這就是爲何咱們選擇使用 spdy
, Express
是 Node.js
適合搭配的實踐標準的服務框架。之因此叫 spdy
是來自於 Google 的 SPDY 協議後來升級成 HTTP/2
。
要在瀏覽器(Firefox, Safari, Chrome, 或者 Edge)中訪問使用 HTTPS ,你須要生成密鑰和證書。去搜索 「ssl 密鑰生成」 或者按照如下步驟去生成密鑰、證書。在該文提供的源碼中沒有上傳生成的密鑰和證書
$ mkdir http2-node-server-push $ cd http2-node-server-push $ openssl genrsa -des3 -passout pass:x -out server.pass.key 2048 ... $ openssl rsa -passin pass:x -in server.pass.key -out server.key writing RSA key $ rm server.pass.key $ openssl req -new -key server.key -out server.csr ... Country Name (2 letter code) [AU]:US State or Province Name (full name) [Some-State]:California ... A challenge password []: ... $ openssl x509 -req -sha256 -days 365 -in server.csr -signkey server.key -out server.crt
按照以上步驟,你就會產生三個 SSL 文件:
server.crt
server.csr
server.key
你就能夠在 Node.js 的 server
腳本中讀取 server.key 和 server.crt。
首先,經過 package.json 初始化項目和下載項目依賴:
npm init -y npm i express@4.14.0 morgan@1.7.0 spdy@3.4.0 --save npm i node-dev@3.1.1 --save-dev
當前的目錄結構以下
/http2-node-server-push /node_modules index.js package.json server.crt server.csr server.key
而後,在 package.json
的 scripts
中添加兩個腳本行,去簡化命令(node-dev、自動重載):
"start": "./node_modules/.bin/node-dev .", "start-advanced": "./node_modules/.bin/node-dev index-advanced.js"
如今就能夠開始使用 Node.js 、 Express.js 、 spdy 編寫這個簡單實現的帶服務器推送 HTTP/2 服務器
首先,建立 index.js
腳本,並引入以及實例化依賴,看看查看上面的項目目錄結構。其中,我使用了 ES6/ES2015 的語法 const
來聲明依賴,若是你不熟悉該聲明語法,你能夠進一步閱讀Top 10 ES6 Features Every Busy JavaScript Developer Must Know。
const http2 = require('spdy') const logger = require('morgan') const express = require('express') const app = express() const fs = require('fs')
而後,設置 morgan logger
來監聽服務器服務了哪些請求。
app.use(logger('dev'))
設置主頁,該頁面顯示了 /pushy
是咱們服務器推送的頁面。
app.get('/', function (req, res) { res.send(`hello, http2! go to /pushy`) })
服務器推送只需簡單的調用 spdy
實現的 res.push
,咱們將文件路徑名傳輸進去做爲第一個參數,瀏覽器會使用這個路徑名來匹配 push promise
資源。res.push()
的第一個參數 /main.js
必定得跟 HTML 文件中須要的文件名相匹配。
而第二個參數是一個可選的對象,設置了該資源的一些資源信息描述。
app.get('/pushy', (req, res) => { var stream = res.push('/main.js', { status: 200, // optional method: 'GET', // optional request: { accept: '*/*' }, response: { 'content-type': 'application/javascript' } }) stream.on('error', function() { }) stream.end('alert("hello from push stream!");') res.end('<script src="/main.js"></script>') })
你能夠看到,stream
對象有兩個方法 on
和 end
。前者監聽了 error
和 finish
事件,然後者則監聽完成傳輸 end
,而後就會 main.js
就會觸發彈窗。
或者,若是你擁有多個數據塊,你能夠選擇使用 res.write()
而後最後使用 res.end()
,其中 res.end()
會自動關閉結束 response
而 res.write()
則讓它保持開啓。(該文的源碼中未實現這種狀況)
最後,讀取 HTTPS 密鑰和證書並使用 spdy
啓動運轉服務器。
var options = { key: fs.readFileSync('./server.key'), cert: fs.readFileSync('./server.crt') } http2 .createServer(options, app) .listen(8080, ()=>{ console.log(`Server is listening on https://localhost:8080. You can open the URL in the browser.`) } )
該實現的關鍵就在於,圍繞着 streams(流)
。不是樹林中的河流,而是指開發者使用的從源頭到客戶端的創建起的數據通道流。若是你幾乎不懂流以及 Node.js
和 Express.js
的 HTTP 的請求和返回信息,你能夠看看該文章 You Don’t Know Node
使用命令 node index.js
或者 npm stat
運行服務端腳本,而後訪問 https://localhost:3000/pushy,就能夠看到彈窗!並且咱們在該路由不存在文件,你能夠查看服務器終端的 logs ,只會有一個請求,而不是沒使用服務器推送的時候的兩個請求(一個 HTML、一個 JS)。
能夠在瀏覽器中檢測收到服務器端推送的行爲。Chrome 啓動開發者工具,打開 Network 標籤,你能夠看到 main.js
不存在綠色時間條,就是說明沒有等待時間 TTFB (Time to First Byte)詳細
再仔細看,能夠看到請求是由 Push
開始發起的(Initiator列查看),沒有使用服務器推送的 HTTP/2 服務器或者 HTTP/1,這一列就會顯示文件名稱,如 index.html
發起的顯示就是 index.html
。
實踐就結束了,使用了 Express 和 Spdy 簡單就實現了推送 JS 資源,而該資源能夠用於後面 HTML 中 <script>
標籤引入的。固然你也能夠在腳本中使用 fs
來讀取文件資源。事實上,這就是做者實現的 Express HTTP/2 靜態資源中間件
設計原理,能夠看看這篇文章
HTTP/2 擁有不少很好的特性,服務器推送是最被看好的特性之一。它的好處就在於當瀏覽器請求頁面的時候,同時發送必需的資源文件(圖片,CSS 樣式,JS 文件),而不須要等待客戶端瀏覽器請求這些資源,從而作到更快的第一次渲染時間
HTTP/2
庫 spdy 讓開發者在基於 Express 的應用能更容易的實現服務器推送特性。
能夠下載參考本文的源碼,而後爲你本身的服務器編寫服務器推送你的資源。
PS:
個人博客