原文: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:
个人博客