虽然HTTP/2目前已经逐渐的在各大网站上开始了使用,但是在目前最新的node . js上仍然处于实验性API,还没有能有效解决生产环境各种问题的应用示例。因此在应用HTTP/2的道路上我自己也遇到了许多坑,下面介绍了项目的主要架构与开发中遇到的问题及解决方式,也许会对你有一点点启示。
虽然W3C的规范中没有规定HTTP/2协议一定要使用ssl加密,但是支持非加密的HTTP/2协议的浏览器实在少的可怜,因此我们有必要申请一个自己的域名和一个ssl证书。
本项目的测试域名是<代码> you.keyin。我> 代码,首先我们去域名提供商那把测试服务器的地址绑定到这个域名上,然后使用我们加密生成一个免费的SSL证书:
sudo certbot certonly——独立- d you.keyin.me
输入必要信息并通过验证之后就可以在<代码>/etc/letsencrypt/生活/you.keyin。我/<代码>下面找到生成的证书了。
亚是一个非常简洁高效的node . js服务器框架,我们可以简单改造一下来让它支持HTTP/2协议:
类KoaOnHttps延伸高雅{ 构造函数(){ 超级(); } 选项(){ 返回{ 关键:fs.readFileSync (require.resolve (“/etc/letsencrypt/生活/you.keyin.me/privkey.pem ')), 证书:fs.readFileSync (require.resolve (“/etc/letsencrypt/生活/you.keyin.me/fullchain.pem ')) }; } 听(args) { const服务器=http2.createSecureServer(这一点。选项,this.callback ()); 返回server.listen (…args); } 重定向(args) { const服务器=http.createServer (this.callback ()); 返回server.listen (…args); } } const应用=new KoaOnHttps (); app.use (sslify ());//? app.listen(443年,()=比;{ 记录器。ok(应用程序开始:,“https://you.keyin.cn”); });//获得所有的http请求,将他们重定向到https app.redirect(80年,()=比;{ 记录器。ok (http重定向服务器开始,' http://you.keyin.me '); }); >之前上述代码简单基于高雅生成了一个HTTP/2服务器,并同时监听80端口,通过sslify中间件的帮助自动将HTTP协议的连接重定向到https协议。
静态文件中间件主要用来返回url所指向的本地静态资源。在http/2服务器中我们可以在访问html资源的时候通过服务器推送(服务器推送)将该页面所依赖的js \ css \字体等资源一起推送回去。具体代码如下:
const发送=要求(“koa-send”); const记录器=要求(“. ./util/记录器”); const{推,acceptsHtml}=要求(“. ./util/助手”); const depTree=要求(“. ./util/depTree”); 模块。出口=(root=")=比;{ 下一个异步函数返回服务(ctx) { 让做=false; 如果(ctx)。方法===巴贰眧 | ctx。方法===玫健?{ 尝试{//当希望收到html时,推送额外资源。 如果(/(\ . html | \/\ w - *)美元/test (ctx.path)) { depTree。currentKey=ctx.path; const编码=ctx。acceptsEncodings (gzip,“缩小”,“身份”);//服务器推送 (const文件的depTree.getDep ()) {//服务器推送之前必须响应!//https://huangxuan.me/2017/07/12/upgrading-eleme-to-pwa/fast-skeleton-painting-with-settimeout-hack 推动(ctx.res。流、文件、编码); } } 做=等待发送(ctx, ctx。路径,{根}); }捕捉(err) { 如果犯错。地位!==404){ logger.error(错); 把犯错; } } } 如果(!){ 等待下一个(); } }; }; >之前需要注意的是,推送的发生永远要先于当前页面的返回。否则服务器推送与客户端请求可能就会出现竞争的情况,降低传输效率。
从静态文件中间件代码中我们可以看的到,服务器推送资源取自depTree这个对象,它是一个依赖记录工具,记录当前页面<代码> depTree。currentKey> 代码所有依赖的静态资源(js、css img…)路径。具体的实现是:
const记录器=要求(“。/记录器”); const db=新地图(); 让currentKey='/'; 模块。出口={ 得到currentKey () { 返回currentKey; }, 设置currentKey(关键="){ currentKey=this.stripDot(关键); }, stripDot (str) { 如果(str)返回”; 返回str.replace(/索引\。html/美元”).replace (/\。/g,“-”); }, addDep (filePath、url、关键=this.currentKey) { 如果(!关键)返回; 关键=this.stripDot(关键); 如果(! db.has(键)){ db。集(关键,新地图()); } const keyDb=db.get(关键); 如果(keyDb。大小祝辞=10){ 记录器。警告(“推动资源限制超过”); 返回: } keyDb。集(filePath url); }, getDep(关键=this.currentKey) { 关键=this.stripDot(关键); const keyDb=db.get(关键); 如果返回[](keyDb==定义); const ret=[]; (filePath、url) (const的keyDb.entries ()) { ret.push ({filePath、url}); } 返回受潮湿腐烂; } };详解基于节点。js的HTTP/2服务器实践