使用 HTTP/2 服务端推送技术加速 Node.js 应用
netjet 服务器推送
四月份，我们宣布了对 [HTTP/2 服务端推送技术](https://www.cloudflare.com/http2/server-push/)的支持，我们是通过 HTTP 的 [Link 头部](https://www.w3.org/wiki/LinkHeader)来实现这项支持的。我的同事 John 曾经通过一个例子演示了[在 PHP 里支持服务端推送功能](https://blog.cloudflare.com/using-http-2-server-push-with-php/)是多么的简单。


![](/data/attachment/album/201609/14/142937id3ze66x6eqd6j63.jpg)


我们想让现今使用 Node.js 构建的网站能够更加轻松的获得性能提升。为此，我们开发了 [netjet](https://www.npmjs.com/package/netjet) 中间件，它可以解析应用生成的 HTML 并自动添加 Link 头部。当在一个示例的 Express 应用中使用这个中间件时，我们可以看到应用程序的输出多了如下 HTTP 头：


![](/data/attachment/album/201609/14/142907thea5xd18s8ot8xo.png)


[本博客](https://blog.cloudflare.com/accelerating-node-js-applications-with-http-2-server-push/)是使用 [Ghost](https://ghost.org/)（LCTT 译注：一个博客发布平台）进行发布的，因此如果你的浏览器支持 HTTP/2，你已经在不知不觉中享受了服务端推送技术带来的好处了。接下来，我们将进行更详细的说明。


netjet 使用了带有定制插件的 [PostHTML](https://github.com/posthtml/posthtml) 来解析 HTML。目前，netjet 用它来查找图片、脚本和外部 CSS 样式表。你也可以用其它的技术来实现这个。


在响应过程中增加 HTML 解析器有个明显的缺点：这将增加页面加载的延时（到加载第一个字节所花的时间)。大多数情况下，所新增的延时被应用里的其他耗时掩盖掉了，比如数据库访问。为了解决这个问题，netjet 包含了一个可调节的 LRU 缓存，该缓存以 HTTP 的 ETag 头部作为索引，这使得 netjet 可以非常快的为已经解析过的页面插入 Link 头部。


不过，如果我们现在从头设计一款全新的应用，我们就应该考虑把页面内容和页面中的元数据分开存放，从而整体地减少 HTML 解析和其它可能增加的延时了。


任意的 Node.js HTML 框架，只要它支持类似 Express 这样的中间件，netjet 都是能够兼容的。只要把 netjet 像下面这样加到中间件加载链里就可以了。



```
var express = require('express');
var netjet = require('netjet');
var root = '/path/to/static/folder';

express()
  .use(netjet({
    cache: {
      max: 100
    }
  }))
  .use(express.static(root))
  .listen(1337);

```

稍微加点代码，netjet 也可以摆脱 HTML 框架，独立工作：



```
var http = require('http');
var netjet = require('netjet');

var port = 1337;
var hostname = 'localhost';
var preload = netjet({
  cache: {
    max: 100
  }
});

var server = http.createServer(function (req, res) {
  preload(req, res, function () {
      res.statusCode = 200;
      res.setHeader('Content-Type', 'text/html');
      res.end('<!doctype html><h1>Hello World</h1>');
  });
});

server.listen(port, hostname, function () {
  console.log('Server running at http://' + hostname + ':' + port+ '/');
});

```

[netjet 文档里](https://www.npmjs.com/package/netjet)有更多选项的信息。


### 查看推送了什么数据


![](/data/attachment/album/201609/14/142909eebxdjlvb8pvz3vg.png)


访问[本文](https://blog.cloudflare.com/accelerating-node-js-applications-with-http-2-server-push/)时，通过 Chrome 的开发者工具，我们可以轻松的验证网站是否正在使用服务器推送技术（LCTT 译注： Chrome 版本至少为 53）。在“Network”选项卡中，我们可以看到有些资源的“Initiator”这一列中包含了`Push`字样，这些资源就是服务器端推送的。


不过，目前 Firefox 的开发者工具还不能直观的展示被推送的资源。不过我们可以通过页面响应头部里的`cf-h2-pushed`头部看到一个列表，这个列表包含了本页面主动推送给浏览器的资源。


希望大家能够踊跃为 netjet 添砖加瓦，我也乐于看到有人正在使用 netjet。


### Ghost 和服务端推送技术


Ghost 真是包罗万象。在 Ghost 团队的帮助下，我把 netjet 也集成到里面了，而且作为测试版内容可以在 Ghost 的 0.8.0 版本中用上它。


如果你正在使用 Ghost，你可以通过修改 config.js、并在`production`配置块中增加 `preloadHeaders` 选项来启用服务端推送。



```
production: {
  url: 'https://my-ghost-blog.com',
  preloadHeaders: 100,
  // ...
}

```

Ghost 已经为其用户整理了[一篇支持文档](http://support.ghost.org/preload-headers/)。


### 总结


使用 netjet，你的 Node.js 应用也可以使用浏览器预加载技术。并且 [CloudFlare](https://blog.cloudflare.com/accelerating-node-js-applications-with-http-2-server-push/) 已经使用它在提供了 HTTP/2 服务端推送了。




---


via: <https://blog.cloudflare.com/accelerating-node-js-applications-with-http-2-server-push/>


作者：[Terin Stock](https://blog.cloudflare.com/author/terin-stock/) 译者：[echoma](https://github.com/echoma) 校对：[wxy](https://github.com/wxy)


本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译，[Linux中国](https://linux.cn/) 荣誉推出


OK
In April, we announced support for [HTTP/2 Server Push](https://www.cloudflare.com/http2/server-push/) via the HTTP `Link`

header. My coworker John has demonstrated how easy it is to [add Server Push to an example PHP](/using-http-2-server-push-with-php/) application.

[CC BY 2.0](https://creativecommons.org/licenses/by/2.0/) [image](https://www.flickr.com/photos/nickyfern/489477622/in/photolist-KfGDm-4WuA7B-4WySRS-a8ijnt-4WuByk-4WuB9M-bE3e6V-665C6K-eeQRx1-b97viM-qzYJ8z-9n2aTS-8EsaQK-aqxV42-jWDkD2-jKyWKv-jzs7yy-9TKcYn-4iAQTa-ECjYJ-96MvXy-bP43v2-rEmKWR-5p3k5r-pVZhDo-os1Njy-5CEEnU-8RhgUX-7JV4jr-9bC7me-sSKqA-72Mppz-maZfyL-6KfUge-dDvw4S-ngyFgY-aPxqsP-7b24QV-5opdXV-sEA7Xj-mZohS-8GFkMr-twfh93-7ZXqtJ-dvRbXj-a8mRiU-4NCzeD-qFXKpj-4n7K7j-34D66Q) by [Nicky Fernandes](https://www.flickr.com/photos/nickyfern/)

We wanted to make it easy to improve the performance of contemporary websites built with [Node.js](https://nodejs.org/). we developed the [netjet](https://github.com/cloudflare/netjet) middleware to parse the generated HTML and automatically add the `Link`

headers. When used with an example Express application you can see the headers being added:

We use [Ghost](https://ghost.org/) to power this blog, so if your browser supports HTTP/2 you have already benefited from Server Push without realizing it! More on that below.

In netjet, we use the [PostHTML](https://github.com/posthtml/posthtml) project to parse the HTML with a custom plugin. Right now it is looking for images, scripts and external stylesheets. You can implement this same technique in other environments too.

Putting an HTML parser in the response stack has a downside: it will increase the page load latency (or "time to first byte"). In most cases, the added latency will be overshadowed by other parts of your application, such as database access. However, netjet includes an adjustable LRU cache keyed by `ETag`

headers, allowing netjet to insert `Link`

headers quickly on pages already parsed.

If you are designing a brand new application, however, you should consider storing metadata on embedded resources alongside your content, eliminating the HTML parse, and possible latency increase, entirely.

Netjet is compatible with any Node.js HTML framework that supports Express-like middleware. Getting started is as simple as adding netjet to the beginning of your middleware chain.

```
var express = require('express');
var netjet = require('netjet');
var root = '/path/to/static/folder';
express()
.use(netjet({
cache: {
max: 100
}
}))
.use(express.static(root))
.listen(1337);
```


With a little more work, you can even use netjet without frameworks.

```
var http = require('http');
var netjet = require('netjet');
var port = 1337;
var hostname = 'localhost';
var preload = netjet({
cache: {
max: 100
}
});
var server = http.createServer(function (req, res) {
preload(req, res, function () {
res.statusCode = 200;
res.setHeader('Content-Type', 'text/html');
res.end('<!doctype html><h1>Hello World</h1>');
});
});
server.listen(port, hostname, function () {
console.log('Server running at http://' + hostname + ':' + port+ '/');
});
```


See the [netjet documentation](https://www.npmjs.com/package/netjet) for more information on the supported options.

Chrome's Developer Tools makes it easy to verify that your site is using Server Push. The Network tab shows pushed assets with "Push" included as part of the initiator.

Unfortunately, Firefox's Developers Tools don't yet directly expose if the resource pushed. You can, however, check for the `cf-h2-pushed`

header in the page's response headers, which contains a list of resources that CloudFlare offered browsers over Server Push.

Contributions to improve netjet or the documentation are greatly appreciated. I'm excited to hear where people are using netjet.

Ghost is one such exciting integration. With the aid of the Ghost team, I've integrated netjet, and it has been available as an opt-in beta since version 0.8.0.

If you are running a Ghost instance, you can enable Server Push by modifying the server's `config.js`

file and add the `preloadHeaders`

option to the `production`

configuration block.

```
production: {
url: 'https://my-ghost-blog.com',
preloadHeaders: 100,
// ...
}
```


Ghost has put together [a support article](http://support.ghost.org/preload-headers/) for Ghost(Pro) customers.

With netjet, your Node.js applications can start to use browser preloading and, when used with CloudFlare, HTTP/2 Server Push today.

At CloudFlare, we're excited to make tools to help increase the performance of websites. If you find this interesting, we are [hiring](https://www.cloudflare.com/join-our-team/) in Austin, Texas; Champaign, Illinois; London; San Francisco; and Singapore.