koa中间件做这样的事相对简单,express 由于next 是同步的,可能实现没有co方便。
基本思路是:当请求过来得时候,记录一下当前时间t1,然后api响应结束时间是t2, 响应时间 = t2 - t1
嗯,实现思路很清晰,另外,最好利用express的中间件机制来实现,这样才能够做到,足够的通用性,监听所有api的响应情况。
那么问题来了,需要知道中间件是从将请求处理,从一个中间件的数据流到另外一个中间件知道数据输出,我们虽然能够轻松地记录到t1,但是t2 却因为这种模型而变得获取困难,难道我们需要再每次响应的时候,特意记录一下t2,想想都觉得是一个非常庞大的工程。
开发express中间件的时候遇到一个问题: 代码如下:
app.use( function ( req, res, next ) {
next( );
console.log( “after next” );
});
app.use( function ( req, res, next ) {
console.log( “before next” );
next( );
});
这里的中间件调用结果是:
before next after next
所以我认为中间件是一个栈结构,next前的代码先执行,然后执行next调用下一个中间件,最后回到当前的中间件,执行next后面的代码。 但是我写了另外一个例子,并不支持我这种理解。 代码如下:
app.use( function ( req, res, next ) {
next( );
console.log( “after next” )
});
app.use( function ( req, res, next ) {
process
.nextTick( function ( ) {
console.log( “before next” )next( );
});
});
执行结果:
after next before next
它的指定执行顺序或者事件循环周期大概是这样的:
// N ∈N+
app.use(function(req, res, next) {
// event loop N: A-start
// event loop N: B-start
process.nextTick(function(){
// event loop N+1:
console.log(“before next”);
// event loop N+1: C-start
// ...
// event loop N+1: C-end
});
// event loop N: B-end
console.log(“after next”);
// event loop N : A-end
})
A执行 碰到next 执行b,b中直接返回。 B结束A结束。
下一个事件循环,B中next执行,程序得以继续往后执行C 。。。。
所以这样的话记录时间就不能以同步的思路去记录了。
确实有个办法解决
exports.responseTime = function () {
return function (req, res, next) {
req._startTime = new Date() // 获取时间 t1
var calResponseTime = function () {
var now = new Date(); //获取时间 t2
var deltaTime = now - req._startTime;
console.log(deltaTime);
}
res.once('finish', calResponseTime);
res.once('close', calResponseTime);
return next();
}
}
使用中间件
app.use(responseTime())
express 里面拥有一个事件机制,通过监听 finish
和 close
事件,可以知道请求到底什么时候结束,监听这个事件,并在事件处理的过程当中,获取t2 ,计算出响应时间,这里值得注意的是,监听的方法是once,即监听一次后,并自动解除监听,如果用了on 方法,每次请求都会产生一次监听,请求多了,内存会泄漏,这里需要十分注意。
那么如果是koa的话,利用generator 或者co 可以比较轻松的以同步的方式去写代码。如下所示:
- common
app.use((ctx, next) => {
const start = new Date();
return next().then(() => {
const ms = new Date() - start;
console.log(`${ctx.method} ${ctx.url} - ${ms}ms`);
});
});
- generatorFunction
app.use(co.wrap(function *(ctx, next) {
const start = new Date();
yield next();
const ms = new Date() - start;
console.log(`${ctx.method} ${ctx.url} - ${ms}ms`);
}));
- async/await(Babel required)
app.use(async (ctx, next) => {
const start = new Date();
await next();
const ms = new Date() - start;
console.log(`${ctx.method} ${ctx.url} - ${ms}ms`);
});
参考:
http://www.jianshu.com/p/f0ebd8261613
https://github.com/i5ting/stuq-koa/blob/master/koa.md
https://cnodejs.org/topic/5684e99204a9c540665c44dd