现在项目遇到了一点问题,表现是命令行有打印,能收到请求,但是不吐数据, 经坛友指点,现在怀疑是资源没释放导致的,所以想加一个超时处理, 然后看了express的教程,貌似可以用connect-timeout中间件解决 但是这个中间件在项目里始终要报can’t set headers after they are sent. 目前也没什么好的解决办法,各位有主意吗?
你异步没处理好吧!
来自酷炫的 CNodeMD
@Aoqin 问题是,我用express-generator生成的新项目引用这个模块仍然报错啊
能放出代码看一下吗?
来自酷炫的 CNodeMD
虽然楼主没贴代码,但根据我踩坑的经验 把调用中间件的next()改成 return next()试试
代码 next 没有处理好
抱歉各位,上午电脑出问题了@cctv1005s @Yuki-Minakami @htoooth 现在也没办法贴代码 ,不过代码大概就这样子:
const timeout = require('connect-timeout');
const timeoutHandler = function(req, res, next) {
if(!req.timedout) next();
}
app.use(timeout('10s'));
app.use(......)
app.use(timeoutHandler)
当天我没注意看文档,昨天意识到了,它是说不建议用作顶级中间件是吧
当作为顶级中间件的时候,是不是每调用一个中间件都要在后面再调用一次handler?@Yuki-Minakami
那他也没说不当顶级中间件该咋使呀。。。T T
我待会试试你的return next();
各位 再看看
首先,你的错误和timeout是不是顶层中间件没关系 第二,如果做顶层中间件也可以,你也可以在所有中间件调用的最后调一次handler,就像这样
app.use(timeout);
app.use(middleware1);
app.use(middleware2);
app.use(middleware3);
app.use(handler)
但这样不推荐,因为如果中间有太多中间件就不好控制整体的执行时间 最后,你目前贴的代码看不出毛病,但八九不离十是哪个异步的中间件前面需要加return
@Yuki-Minakami 那应该在哪里使用timeout中间件呢? 应该是在具体业务流程开始之前使用吗?
帖一下全部代码:
**app.js**
var express = require('express');
var path = require('path');
var favicon = require('serve-favicon');
var logger = require('morgan');
var cookieParser = require('cookie-parser');
var bodyParser = require('body-parser');
const timeout = require('connect-timeout');
var index = require('./routes/index');
var users = require('./routes/users');
var app = express();
// view engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'pug');
// uncomment after placing your favicon in /public
//app.use(favicon(path.join(__dirname, 'public', 'favicon.ico')));
const handleTimeout = (req, res, next) => {
if(!req.timedout) next();
}
app.use(logger('dev'));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'public')));
app.use(timeout('5s'));
app.use('/', index);
app.use('/users', users);
// catch 404 and forward to error handler
app.use(function(req, res, next) {
var err = new Error('Not Found');
err.status = 404;
return next(err);
});
// error handler
app.use(handleTimeout);
app.use(function(err, req, res, next) {
// set locals, only providing error in development
res.locals.message = err.message;
res.locals.error = req.app.get('env') === 'development' ? err : {};
// render the error page
res.status(err.status || 500);
return res.render('error');
});
module.exports = app;
**users.js**
var express = require('express');
var router = express.Router();
/* GET users listing. */
router.get('/', function(req, res, next) {
setTimeout(function() {
return res.send('respond with a resource');
}, 10000);
});
module.exports = router;
当我访问users的时候,页面在5秒时会打印503报错页,是对的 但是10秒的时候,app会报错:
_http_outgoing.js:504
throw new Error('Can\'t set headers after they are sent.');
^
Error: Can't set headers after they are sent.
at validateHeader (_http_outgoing.js:504:11)
at ServerResponse.setHeader (_http_outgoing.js:511:3)
at ServerResponse.header (D:\test\app\node_modules\express\lib\response.js:730:10)
at ServerResponse.send (D:\test\app\node_modules\express\lib\response.js:170:12)
at Timeout._onTimeout (D:\test\app\routes\users.js:7:16)
at ontimeout (timers.js:488:11)
at tryOnTimeout (timers.js:323:5)
at Timer.listOnTimeout (timers.js:283:5)
npm ERR! code ELIFECYCLE
npm ERR! errno 1
npm ERR! [email protected] start: `node ./bin/www`
npm ERR! Exit status 1
npm ERR!
npm ERR! Failed at the [email protected] start script.
npm ERR! This is probably not a problem with npm. There is likely additional logging output above.
npm ERR! A complete log of this run can be found in:
npm ERR! C:\Users\lz\AppData\Roaming\npm-cache\_logs\2017-06-05T07_01_18_145Z-debug.log
@Aoqin @cctv1005s @Yuki-Minakami @htoooth ,大家再看看,感激不尽
问题就是出在10秒后会调用res.send方法,但是应该怎么写呢,在正常的逻辑中,肯定要涉及到渲染输出到res的; 根据这个timeoutHandler的写法来看,是不是在设计的时候应该把渲染输出到res和数据库操作等有可能超时的操作分离,然后在每一处可能造成超时的中间件调用完成之后加上app.use(timeoutHandler);?
嗯,大致明白了,和开始想的不大一样,不好意思前面可能误导你了
超时时间设置了5秒,ok,5秒后超时返回了错误码,这个http请求就结束了 然而10秒后还有个res需要输出,对一个已经结束的进行响应,这就是错误的原因
要改的话就把app.use(’/users’, users);这句放到外面
app.use(timeout('5s'));
app.use('/', index);
// catch 404 and forward to error handler
app.use(function(req, res, next) {
var err = new Error('Not Found');
err.status = 404;
return next(err);
});
// error handler
app.use(handleTimeout);
app.use('/users', users);
这样10秒后就没有cant send header again的错误了,但这样就舍本逐末了
根本问题应该是面对一个未知耗时的异步操作,怎么用connect-time来管理 这部分让我回家想想…
你报的这个错误我在egg里不使用yield的时候也遇到过
app.set(‘view engine’, ‘pug’);这是个什么模板
@Yuki-Minakami 有点搅,这中间件github才两百星,3个未解决的issue是关于这个问题的,貌似啊,记不清了
@yangkuo1993 原来的jade
@wangchaoduo 能具体回忆下吗
这个目前还真没啥好办法, 你这个场景其实只要想办法让10秒后的那个回调不执行就好了 具体可以使用事件监听,如果超过了timeout时间,就清除掉后面的setTimeout
@lzszone 他的问题对你的应该没啥帮助
@Yuki-Minakami 对,思路是这样的,他源码很短90行,然后看了下,感觉这玩意不好用。。。怪不得才200星,剩下的我自己想吧,谢谢
楼主我写了篇文章关于超时处理的,可以看看 https://cnodejs.org/topic/5936cd57538dbcaa6bc7dd8f
你可以设个类似开关的值,只要res.send后就变为false,发送前先判定次
来自酷炫的 CNodeMD