当前位置: 首页 > 科技观察

通过Node.js、Express.js实现HTTP-2ServerPush

时间:2023-03-21 10:36:04 科技观察

什么是HTTP/2ServerPushHTTP/2是Web开发的新标准,有很多很好的优点,可以让Web访问更快,开发工作更简单。例如引入多路复用传输不需要合并资源,服务器推送(ServerPush)资源让浏览器预加载。本文并未描述HTTP/2的所有优点。您可以在上一篇文章中了解有关{%post_linkhttp2-node-express%}的更多信息。本文重点介绍在Node.js环境中使用Express.js和HTTP/2库spdy。服务器推送通过在一个HTTP/2请求中捆绑多个资源来工作。在后台,服务器发送一个PUSH_PROMISE,无论HTML文件是否需要资源,客户端(包括浏览器)都可以利用它。如果浏览器检测到需要该资源,它将匹配从服务器收到的PROMISE,并使该资源表现得像普通的浏览器Get请求资源一样。很明显,如果匹配到一个push,浏览器不需要重新请求,直接使用客户端缓存。这里有几篇关于ServerPush的好处的文章:ServerPush的好处是什么?宣布支持HTTP/2服务器推送使用HTTP2.0服务器推送进行创新这是关于在Node.js中实现服务器推送的动手教程。为了清楚和简单起见,我们只实现一个路由地址为/pushy的Node.js和Express.js服务器,它会推送一个JS文件。如前所述,我们将使用HTTP/2库spdy。HTTP/2和Node.js首先解释一下为什么在Node.js环境下选择HTTP/2库spdy。目前,有两个主要的库为Node.js实现了HTTP/2:spdyhttp2这两个库与Node.js核心模块的http和https模块api非常相似。这意味着如果您不使用Express,则这两个库之间没有区别。不过spdy库支持HTTP/2和Express,而http2库目前不支持Express。这就是我们选择使用spdy的原因,Express是Node.js适合与之配对的实践标准服务框架。之所以叫spdy,是源于谷歌的SPDY协议,后来升级为HTTP/2。HTTPS密钥和证书要在浏览器(Firefox、Safari、Chrome或Edge)中访问HTTPS,您需要生成密钥和证书。前往搜索“sslkeygeneration”或按照以下步骤生成密钥和证书。本文提供的源码中没有上传生成的密钥和证书$mkdirhttp2-node-server-push$cdhttp2-node-server-push$opensslgenrsa-des3-passoutpass:x-outserver.pass.key2048...$opensslrsa-passinpass:x-inserver.pass.key-outserver.keywritingRSAkey$rmserver.pass.key$opensslreq-new-keyserver.key-outserver.csr...CountryName(2lettercode)[AU]:USStateorProvinceName(fullname)[一些-State]:California...Achallengepassword[]:...$opensslx509-req-sha256-days365-inserver.csr-signkeyserver.key-outserver.crt按照上面的步骤,你会生成三个SSL文件:server.crtserver.csrserver.key你可以在Node.js服务器脚本中读取server.key和server.crt。构建项目首先初始化项目,通过package.json下载项目依赖:npminit-ynpmiexpress@4.14.0morgan@1.7.0spdy@3.4.0--savenpminode-dev@3.1.1--save-dev当前目录结构如下/http2-node-server-push/node_modulesindex.jspackage.jsonserver.crtserver.csrserver.key然后在package.json的scripts中添加两行脚本来简化命令(node-dev,自动reload):”start":"./node_modules/.bin/node-dev.","start-advanced":"./node_modules/.bin/node-devindex-advanced.js"现在你可以开始使用Node.js,Express了。js,spdy用serverpushHTTP/2serverscripting写了这个简单的实现首先创建index.js脚本,并引入并实例化依赖,见上面的项目目录结构。其中,我使用了ES6/ES2015语法const来声明依赖。如果您不熟悉声明语法,可以阅读每个忙碌的JavaScript开发人员必须知道的10大ES6功能。consthttp2=require('spdy')constlogger=require('morgan')constexpress=require('express')constapp=express()constfs=require('fs')然后,设置morgan记录器以监视服务器服务的请求.app.use(logger('dev'))设置首页,可见/pushy是我们服务器推送的页面。app.get('/',function(req,res){res.send(`hello,http2!goto/pushy`)})服务器推送只是调用spdy实现的res.push,我们将文件路径名作为第一个参数传入,浏览器将使用此路径名来匹配推送承诺资源。res.push()的第一个参数/main.js必须匹配HTML文件中所需的文件名。第二个参数是可选对象,设置资源的一些资源信息描述。app.get('/pushy',(req,res)=>{varstream=res.push('/main.js',{status:200,//optionalmethod:'GET',//optionalrequest:{accept:'*/*'},response:{'content-type':'application/javascript'}})stream.on('error',function(){})stream.end('alert("来自推流的你好!");')res.end('')})可以看到,stream对象有on和end两个方法。前者监听error和finish事件,后者监听发送端完成,然后main.js会触发弹窗。或者,如果你有多个数据块,你可以选择使用res.write()最后使用res.end(),其中res.end()会自动关闭结束响应,而res.write()会保持打开状态.(本文源码中没有实现)最后读取HTTPS密钥和证书,用spdy启动服务器。varoptions={key:fs.readFileSync('./server.key'),cert:fs.readFileSync('./server.crt')}http2.createServer(options,app).listen(8080,()=>{console.log(`Serverislisteningonhttps://localhost:8080.YoucanopentheURLinthebrowser.`)})此实现的关键在于流。不是树林中的河流,而是开发人员从源到客户使用的已建立的数据通道流。如果你对流式传输和Node.js、Express.js的HTTP请求和返回信息了解不多开始比较HTTP/2ServerPush使用命令nodeindex.js或npmstat运行服务器脚本,然后访问https://localhost:3000/pushy,可以看到弹窗!而且这个路由我们没有文件,可以查看服务端的日志,只有一次请求,没有使用服务端时不会有两次请求(一个HTML,一个JS)。可以在浏览器中检测到接收服务器端推送的行为。启动Chrome开发者工具,打开Network选项卡,可以看到main.js里面没有绿色的时间条,说明没有等待时间TTFB(TimetoFirstByte),仔细看可以看到请求是由Push发起的(在Initiator栏查看),如果没有使用HTTP/2服务器或者服务器推送的HTTP/1,该栏会显示文件名,比如index.htmlindex发起的显示。html是index.html。实践结束,使用Express和Spdy简单推送JS资源,这个资源可以用于后面HTML中