我们的目的是让优化 Node.js 网站变得更容易。我们开发了 netjet 中间件来解析 HTML 并自动插入 Link 头。在这个 Express 的例子中,可以看见增加的头部:
我们使用 Ghost 来增强本博客的性能,如果你的浏览器支持 HTTP2,你已经在不知不觉中受益于服务端推送了(请见博客原文——译者注)。下面还有更多。
在 netjet 中,我们使用 PostHTML 项目与一个普通插件一起解析 HTML。现在它查找的是图片、脚本和外链样式表。你也可以在其它环境中实现同样的技术。
在 HTTP 的响应中解析 HTML 有一个缺点:增加加载延迟(或者“首字节到达时间”)。大多数情况下,增加的延迟会被其它环节掩盖,如数据库访问。然而,netjet 包含了一个可伸缩的由 ETag 头实现的 LRU 缓存,允许对于了已经解析过页面快速插入 Link 头。
如果你在设计一个很有野心的应用,你应该在内容中嵌入资源上考虑存储源信息,移除 HTML 的解析过程,以及可能的延迟增加。
Netjet 与任何支持像 Express 中间件一样的 Node.js 框架兼容。入门就如把 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,只需要一点点额外的工作。
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 文档。
查看推送的资源
Chrome 的开发者工具使得鉴别网站是否使用服务端推送变得很简单。网络选项卡在 initiator 列的开头以 “push” 来标记被推送的资源。
不幸的是,Firefox 的开发者工具没有直接指明资源是否是被推送的。但是你可以查看页面响应头的 cf-h2-pushed 值,它包含了 CloudFlare 推送给浏览器的资源列表。
欢迎为 netjet 及其文档贡献力量。我很愿意听说有人在用 netjet。
Ghost 和服务端推送
Ghost(指 https://ghost.org/ —— 译者注) 是一个令人兴奋的整合平台。在 Ghost 团队的帮助下,我已经把 netjet 整合了进去,在 0.8.0 版本后已经可以作为一个可选的配置。
如果你在运行一个 Ghost 实例,你可以通过修改 config.js 文件、在 production 配置块中增加 preloadHeaders 选项的方式开启服务端推送。
production: {
url: 'https://my-ghost-blog.com',
preloadHeaders: 100,
// ...
}
Ghost 为用户提供了一个帮助文档 。
补充一下其它加还node.js的方法
异步
根据设计 Node.js 是单线程的。基于这点,同步代码会堵塞整个应用。比如说,多数的文件系统 API 都有它们的同步版本。下面代码演示了文件读取的同步和异步两种操作:
// Asynchronous
fs.readFile('file.txt', function(err, buffer) {
var content = buffer.toString();
});
// Synchronous
var content = fs.readFileSync('file.txt').toString();
不过要是你执行那种长时间的阻塞操作,主线程就会被阻塞到这些操作完成为止。这大大降低你应用的性能。所以,最好确保你的代码里用的都是异步版本 API,最起码你应该在性能节点异步。而且,你在选用第三方模块的时候也要很小心。因为当你想方设法把同步操作从你代码中剔除之后,一个外部库的同步调用会让你前功尽弃,降低你的应用性能。
缓存
如果你用到一些不经常变化的数据,你应该把它们缓存起来,改善性能。比如说,下面的代码是获取最新帖子并显示的例子:
var router = express.Router();
router.route('/latestPosts').get(function(req, res) {
Post.getLatest(function(err, posts) {
if (err) {
throw err;
}
res.render('posts', { posts: posts });
});
});
如果你不经常发贴的话,你可以把帖子列表缓存起来,然后一段时间之后再把它们清理掉。比如,我们可以用 Redis 模块来达到这个目的。当然,你必须在你的服务器上装 Redis。然后你可以用叫做 node_redis 的客户端来保存键/值对。下面的例子演示我们怎么缓存帖子:
var redis = require('redis'),
client = redis.createClient(null, null, { detect_buffers: true }),
router = express.Router();
router.route('/latestPosts').get(function(req,res){
client.get('posts', function (err, posts) {
if (posts) {
return res.render('posts', { posts: JSON.parse(posts) });
}
Post.getLatest(function(err, posts) {
if (err) {
throw err;
}
client.set('posts', JSON.stringify(posts));
res.render('posts', { posts: posts });
});
});
});
看到了吧,我们首先检查 Redis 缓存,看看是否有帖子。如果有,我们从缓存中拿这些帖子列表。否则我们就检索数据库内容,然后把结果缓存。此外,一定时间之后,我们可以清理 Redis 缓存,这样就可以更新内容了。
gzip 压缩
开启 gzip 压缩对你的 Web 应用会产生巨大影响。当一个 gzip 压缩浏览器请求某些资源的时候,服务器会在响应返回给浏览器之前进行压缩。如果你不用 gzip 压缩你的静态资源,浏览器拿到它们可能会花费更长时间。
在 Express 应用中,我们可以用内建 express.static() 中间件来处理静态内容。此外,还可以用 compression 中间件压缩和处理静态内容。下面是使用例:
var compression = require('compression');
app.use(compression()); //use compression
app.use(express.static(path.join(__dirname, 'public')));
总结
使用 netjet 后,你的 Node.js 应用可以开发使用浏览器预加载,在 CloudFlare 中,HTTP2 的服务端推送可以被开启。