给 connect 的 static 模块加上url路径前缀
by 司徒正美
at 2012-07-14 10:06:00
original http://www.cnblogs.com/rubylouvre/archive/2012/07/14/2591139.html
估计我们使用 connect 都会很自然地按照官方的例子使用静态文件模块 static:
var connect = require('connect');connect( connect.static(__dirname), function (req, res) { res.writeHead(200, {'Content-Type': 'text/plain'}); res.end('Hello World\n'); }).listen(8124);console.log('Server running at http://127.0.0.1:8124/');
基准性能
为了评测 static 的性能,我们需要又一个基准对比。
官方最纯洁的 helloworld
我们使用 nodejs 官方文档给出的 helloworld 做最基础的参照:
ar http = require('http');http.createServer(function (request, response) { response.writeHead(200, {'Content-Type': 'text/plain'}); response.end('Hello World\n');}).listen(8124);console.log('Server running at http://127.0.0.1:8124/');
最纯洁的 connect helloworld
不使用任何中间件模块
var connect = require('connect');connect(function (req, res) { res.writeHead(200, {'Content-Type': 'text/plain'}); res.end('Hello World\n');}).listen(8124);console.log('Server running at http://127.0.0.1:8124/');
结合 domain 模块的 connect helloworld
var connect = require('connect');var createDomain = require('domain').create;connect( function (req, res, next) { var domain = createDomain(); domain.on('error', function (err) { console.log('errrrrr', err); res.statusCode = 500; res.end(err.message + '\n'); domain.dispose(); }); domain.run(next); }, function (req, res, next) { if (req.url === '/error') { process.nextTick(function () { res.end('params: ' + req.query.abc); }); return; } res.writeHead(200, {'Content-Type': 'text/plain'}); res.end('Hello World\n'); }).listen(8124);console.log('Server running at http://127.0.0.1:8124/');
测试结果
官方最纯洁的 helloworld: 7851.56 qps
$ siege -b -c10 -t10S http://127.0.0.1:8124/** SIEGE 2.72** Preparing 10 concurrent users for battle.The server is now under siege...Lifting the server siege... done.Transactions: 78123 hitsAvailability: 100.00 %Elapsed time: 9.95 secsData transferred: 0.89 MBResponse time: 0.00 secsTransaction rate: 7851.56 trans/secThroughput: 0.09 MB/secConcurrency: 9.93Successful transactions: 78123Failed transactions: 0Longest transaction: 0.09Shortest transaction: 0.00
最纯洁的 connect helloworld: 6808.19 qps
$ siege -b -c10 -t10S http://127.0.0.1:8124/** SIEGE 2.72** Preparing 10 concurrent users for battle.The server is now under siege...Lifting the server siege... done.Transactions: 78123 hitsAvailability: 100.00 %Elapsed time: 9.95 secsData transferred: 0.89 MBResponse time: 0.00 secsTransaction rate: 7851.56 trans/secThroughput: 0.09 MB/secConcurrency: 9.93Successful transactions: 78123Failed transactions: 0Longest transaction: 0.09Shortest transaction: 0.00
使用 domain 模块的 connect helloworld: 5601.35 qps
$ siege -b -c10 -t10S http://127.0.0.1:8124/** SIEGE 2.72** Preparing 10 concurrent users for battle.The server is now under siege...Lifting the server siege... done.Transactions: 65699 hitsAvailability: 100.00 %Elapsed time: 9.65 secsData transferred: 0.75 MBResponse time: 0.00 secsTransaction rate: 6808.19 trans/secThroughput: 0.08 MB/secConcurrency: 9.96Successful transactions: 65699Failed transactions: 0Longest transaction: 0.05Shortest transaction: 0.00
带 static 的 connect helloworld: 3636.98 qps
$ siege -b -c10 -t10S http://127.0.0.1:8124/** SIEGE 2.72** Preparing 10 concurrent users for battle.The server is now under siege...Lifting the server siege... done.Transactions: 34915 hitsAvailability: 100.00 %Elapsed time: 9.60 secsData transferred: 0.40 MBResponse time: 0.00 secsTransaction rate: 3636.98 trans/secThroughput: 0.04 MB/secConcurrency: 9.97Successful transactions: 34915Failed transactions: 0Longest transaction: 0.06Shortest transaction: 0.00
为什么性能降低了50%
晕,为什么加上了 static 模块,性能会降低了50%这么多?
查看 static.send() 源代码:
// "hidden" fileif (!hidden && '.' == basename(path)[0]) return next();fs.stat(path, function(err, stat){ // mime type type = mime.lookup(path); // ignore ENOENT if (err) { if (fn) return fn(err); return ('ENOENT' == err.code || 'ENAMETOOLONG' == err.code) ? next() : next(err); // redirect directory in case index.html is present } else if (stat.isDirectory()) { if (!redirect) return next(); res.statusCode = 301; res.setHeader('Location', url.pathname + '/'); res.end('Redirecting to ' + url.pathname + '/'); return; }
static 模块每次都需要一次文件IO,判断文件是否存在,这是多么损耗性能啊。
增加静态文件url路径前缀
既然找到性能问题所在,就可以解决此问题了。对症下药,无需让所有请求都经过 static 处理即可。
给 static 增加一个url前缀判断,例如 /public/images/logo.jpg 只有前缀是 /public 的 url 请求才需要进入 static 模块处理。
那么我们改进后的代码应该是这样的:
var connect = require('connect');var app = connect();app.use('/public', connect.static(__dirname));app.use(function (req, res) { res.writeHead(200, {'Content-Type': 'text/plain'}); res.end('Hello World\n');}).listen(8124);console.log('Server running at http://127.0.0.1:8124/');
性能如何? wow 6749.03 qps, 几乎和 connect hellowrold 一致。done!
$ siege -b -c10 -t10S http://127.0.0.1:8124/** SIEGE 2.72** Preparing 10 concurrent users for battle.The server is now under siege...Lifting the server siege... done.Transactions: 66073 hitsAvailability: 100.00 %Elapsed time: 9.79 secsData transferred: 0.76 MBResponse time: 0.00 secsTransaction rate: 6749.03 trans/secThroughput: 0.08 MB/secConcurrency: 9.97Successful transactions: 66073Failed transactions: 0Longest transaction: 0.03Shortest transaction: 0.00
重现一下之前的性能问题,访问 /public/foo 即可重现。
$ siege -b -c10 -t10S http://127.0.0.1:8124/public/foo** SIEGE 2.72** Preparing 10 concurrent users for battle.The server is now under siege...Lifting the server siege... done.Transactions: 37773 hitsAvailability: 100.00 %Elapsed time: 9.59 secsData transferred: 0.43 MBResponse time: 0.00 secsTransaction rate: 3938.79 trans/secThroughput: 0.05 MB/secConcurrency: 9.97Successful transactions: 37773Failed transactions: 0Longest transaction: 0.05Shortest transaction: 0.00
有爱
记得给 connect.static 加上一个url路径前缀喔!
^_^ 希望本文对你有用。