通过 Node.js, Express.js 实现 HTTP/2 Server Push

简介:


什么是 HTTP/2 Server Push

HTTP/2 是 Web 开发的新标准,拥有很多不错的优点能够让 Web 访问更快且开发的工作更轻松简单。比如,引入多路复用传输不用合并资源,服务器推送(Server Push)资源让浏览器预加载。

该文不会讲述 HTTP/2 的所有优势。你可以通过上篇文章了解更多{% post_link http2-node-express %}。该文主要关注于在 Node.js环境使用 Express.js 和 HTTP/2 库 spdy。

服务器推送(Server Push)工作方式是通过在一个 HTTP/2 请求中捆绑多个资源。在底层,服务器会发送一个 PUSH_PROMISE,客户端(包括浏览器)就可以利用它且不基于 HTML 文件是否需要该资源。如果浏览器检测到需要该资源,就会匹配到收到的服务器推送的 PROMISE 然后让该资源表现的就像正常的浏览器 Get 请求资源。显而易见,如果匹配到有推送,浏览器就不需要重新请求,然后直接使用客户端缓存。这推荐几篇文章关于服务器推送(Server Push)的好处:

  • What’s the benefit of Server Push?
  • Announcing Support for HTTP/2 Server Push
  • Innovating with HTTP 2.0 Server Push

这是个关于在 Node.js 实现服务器推送(Server Push)实践教程。为了更清晰精简,我们只实现一个路由地址 /pushy 的 Node.js和 Express.js 服务器,它会推送一个 JS 文件,正如之前所说,我们会用到一个 HTTP/2 库 spdy。

HTTP/2 和 Node.js

先解释一下,为啥在 Node.js 环境选择 HTTP/2 库 spdy。当前来说,为 Node.js 主要有两个库实现了 HTTP/2 :

  • spdy
  • http2

两个库都跟 Node.js 核心模块的 http 和 https 模块 api 很相似。这就意味着如果你不使用 Express ,这两个库就没什么区别。然而, spdy 库支持 HTTP/2 和 Express,而 http2 库当前不支持 Express。这就是为什么我们选择使用 spdy , Express 是Node.js 适合搭配的实践标准的服务框架。之所以叫 spdy是来自于 Google 的 SPDY 协议后来升级成 HTTP/2。

HTTPS密钥和证书

要在浏览器(Firefox, Safari, Chrome, 或者 Edge)中访问使用 HTTPS ,你需要生成密钥和证书。去搜索 “ssl 密钥生成” 或者按照以下步骤去生成密钥、证书。在该文提供的源码中没有上传生成的密钥和证书

 
  1. $ mkdir http2-node-server-push  
  2. $ cd http2-node-server-push 
  3. $ openssl genrsa -des3 -passout pass:x -out server.pass.key 2048 
  4. ... 
  5. $ openssl rsa -passin pass:x -in server.pass.key -out server.key 
  6. writing RSA key 
  7. $ rm server.pass.key 
  8. $ openssl req -new -key server.key -out server.csr 
  9. ... 
  10. Country Name (2 letter code) [AU]:US 
  11. State or Province Name (full name) [Some-State]:California 
  12. ... 
  13. A challenge password []: 
  14. ... 
  15. $ 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 初始化项目和下载项目依赖:

 
  1. npm init -y 
  2. npm i express@4.14.0 morgan@1.7.0 spdy@3.4.0 --save 
  3. npm i node-dev@3.1.1 --save-dev 

当前的目录结构如下

 
  1. /http2-node-server-push 
  2. /node_modules 
  3. index.js 
  4. package.json 
  5. server.crt 
  6. server.csr 
  7. server.key 

然后,在 package.json 的 scripts 中添加两个脚本行,去简化命令(node-dev、自动重载):

 
  1. "start""./node_modules/.bin/node-dev ."
  2. "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。

 
  1. const http2 = require('spdy'
  2. const logger = require('morgan'
  3. const express = require('express'
  4. const app = express() 
  5. const fs = require('fs'

然后,设置 morgan logger 来监听服务器服务了哪些请求。

 
  1. app.use(logger('dev')) 

设置主页,该页面显示了 /pushy 是我们服务器推送的页面。

 
  1. app.get('/'function (req, res) { 
  2.   res.send(`hello, http2! go to /pushy`) 
  3. }) 

服务器推送只需简单的调用 spdy 实现的 res.push ,我们将文件路径名传输进去作为第一个参数,浏览器会使用这个路径名来匹配push promise 资源。res.push() 的第一个参数 /main.js 一定得跟 HTML 文件中需要的文件名相匹配。

而第二个参数是一个可选的对象,设置了该资源的一些资源信息描述。

 
  1. app.get('/pushy', (req, res) => { 
  2.   var stream = res.push('/main.js', { 
  3.     status: 200, // optional 
  4.     method: 'GET', // optional 
  5.     request: { 
  6.       accept: '*/*' 
  7.     }, 
  8.     response: { 
  9.       'content-type''application/javascript' 
  10.     } 
  11.   }) 
  12.   stream.on('error'function() { 
  13.   }) 
  14.   stream.end('alert("hello from push stream!");'
  15.   res.end('<script src="/main.js"></script>'
  16. }) 

你可以看到,stream 对象有两个方法 on 和 end。前者监听了 error 和 finish 事件,而后者则监听完成传输 end,然后就会main.js 就会触发弹窗。

或者,如果你拥有多个数据块,你可以选择使用 res.write() 然后最后使用 res.end(),其中 res.end() 会自动关闭结束response 而 res.write() 则让它保持开启。(该文的源码中未实现这种情况)

最后,读取 HTTPS 密钥和证书并使用 spdy 启动运转服务器。

 
  1. var options = { 
  2.   key: fs.readFileSync('./server.key'), 
  3.   cert: fs.readFileSync('./server.crt'
  4.  
  5. http2 
  6.   .createServer(options, app) 
  7.   .listen(8080, ()=>{ 
  8.     console.log(`Server is listening on https://localhost:8080. 
  9.     You can open the URL in the browser.`) 
  10.   } 

该实现的关键就在于,围绕着 streams(流)。不是树林中的河流,而是指开发者使用的从源头到客户端的建立起的数据通道流。如果你几乎不懂流以及 Node.js 和 Express.js 的 HTTP 的请求和返回信息

启动和对比 HTTP/2 Server Push

使用命令 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 来读取文件资源。

总结

HTTP/2 拥有很多很好的特性,服务器推送是最被看好的特性之一。它的好处就在于当浏览器请求页面的时候,同时发送必需的资源文件(图片,CSS 样式,JS 文件),而不需要等待客户端浏览器请求这些资源,从而做到更快的第一次渲染时间

HTTP/2 库 spdy 让开发者在基于 Express 的应用能更容易的实现服务器推送特性。


作者:Azat Mardan

来源:51CTO

相关文章
|
18天前
|
开发框架 JavaScript 安全
js开发:请解释什么是Express框架,以及它在项目中的作用。
【4月更文挑战第24天】Express是Node.js的Web开发框架,简化路由管理,支持HTTP请求处理。它包含中间件系统用于日志、错误处理和静态文件服务,集成多种模板引擎如EJS、Jade、Pug。框架还提供安全中间件提升应用安全,并具有良好的可扩展性,便于项目功能扩展和开发效率提升。
25 3
|
19天前
|
JSON JavaScript 前端开发
❤Nodejs 第九章(token身份认证和express-jwt的安装认识)
【4月更文挑战第9天】Node.js第九章介绍了Token身份认证,特别是JWT(JSON Web Token)作为跨域认证的解决方案。JWT由Header、Payload和Signature三部分组成,用于在客户端和服务器间安全传输用户信息。前端收到JWT后存储在localStorage或sessionStorage中,并在请求头中发送。Express-JWT是一个中间件,用于解析JWT。基本用法包括设置secret和algorithms。注意安全问题,避免混合使用不同算法以防止降级攻击。
38 0
|
4天前
|
开发框架 JavaScript 中间件
中间件应用Koa.js(Node.js)
我们添加了两个中间件。第一个中间件记录请求的开始时间,并在下一个中间件执行完毕后计算并打印出请求的总时间。第二个中间件与之前的示例相同,它设置响应体为 "Hello World"
20 6
|
14天前
|
JavaScript 前端开发 持续交付
【专栏】Vue.js和Node.js如何结合构建现代Web应用
【4月更文挑战第27天】本文探讨了Vue.js和Node.js如何结合构建现代Web应用。Vue.js作为轻量级前端框架,以其简洁易懂、组件化开发、双向数据绑定和虚拟DOM等特点受到青睐;而Node.js是高性能后端平台,具备事件驱动、非阻塞I/O、丰富生态系统和跨平台优势。两者结合实现前后端分离,高效通信,并支持热更新、持续集成、跨平台和多端适配,为开发高性能、易维护的Web应用提供强有力的支持。
|
4天前
|
JavaScript 中间件 API
中间件应用Express.js(Node.js)
我们定义了一个名为 `logger` 的中间件函数。它接受请求对象、响应对象以及下一个中间件函数作为参数。当接收到请求时,它会打印出请求的 HTTP 方法和 URL,然后调用 `next()` 函数来将控制权传递给下一个中间件或路由处理器。我们使用 `app.use()` 方法将 `logger` 中间件添加到了应用级别的中间件堆栈中,这意味着它将对所有请求生效。
12 3
|
11天前
|
JavaScript 前端开发 开发工具
【JavaScript 技术专栏】Node.js 基础与实战
【4月更文挑战第30天】本文介绍了Node.js的基础及应用,包括事件驱动的非阻塞I/O、单线程模型和模块系统。内容涵盖Node.js的安装配置、核心模块(如http、fs、path)及实战应用,如Web服务器、文件操作和实时通信。文章还讨论了Node.js的优劣势、与其他技术的结合,并通过案例分析展示项目实施流程。总结来说,Node.js是高效后端开发工具,适合构建高并发应用,其广阔的应用前景值得开发者探索。
|
12天前
|
JavaScript API 开发者
深入了解Node.js的文件系统:Node.js文件系统API的使用与探索
【4月更文挑战第30天】本文深入探讨了Node.js的文件系统API,介绍了如何引入`fs`模块进行文件操作。内容包括异步读取和写入文件、删除文件、创建目录以及使用文件流进行高效操作。此外,还提到了文件系统的监视功能,帮助开发者全面掌握在Node.js中处理文件和目录的方法。
|
12天前
|
开发框架 JavaScript 中间件
深入探索Node.js的Express框架:使用与中间件详解
【4月更文挑战第30天】本文深入探讨了Node.js的Express框架,介绍了其作为Web开发的强大工具,主要聚焦于基本使用和中间件。Express是基于Node.js的Web应用框架,用于构建高效的应用和API。文章详细讲解了如何安装Express,创建简单应用,以及中间件的工作原理和应用,包括中间件的顺序、错误处理和挂载位置。此外,还提到了使用第三方中间件扩展功能。理解Express基础和中间件对于开发高质量Web应用至关重要。
|
18天前
|
Web App开发 JavaScript 前端开发
js开发:请解释什么是Node.js,以及它的应用场景。
Node.js是基于V8引擎的JavaScript运行时,用于服务器端编程。以其事件驱动、非阻塞I/O模型著称,适用于高并发和实时应用。常见用途包括:构建Web服务器、实时应用(如聊天)、API服务、微服务、工具和命令行应用,以及搭配Electron开发桌面软件。
20 1
|
27天前
|
JavaScript 中间件 API
node.js之express的基础知识
node.js之express的基础知识