【问题标题】:How to make requests timeout after x seconds node.js/express如何在 x 秒后使请求超时 node.js/express
【发布时间】:2016-09-09 12:31:55
【问题描述】:

我的中间件布局大概是这样的:

let express = require('express');
let app = express();

app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
app.use(cookieParser());

app.use(myBigMiddleware);   // this will call 3rd party service and can time-out
app.use(myAfterwardsMiddleware);
[maybe more post-processing middleware]

我想要实现的是任何在 XX 秒内没有得到响应的请求都应该被取消 + 一些 HTTP 错误应该被发送到浏览器/客户端。

我发现https://github.com/expressjs/timeout 似乎可以做到这一点,但不知何故我太笨了,无法使用它。我不明白如何取消中间件管道,而是执行res.status(503).end(); 之类的操作。

调整以上内容:

app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
app.use(cookieParser());

app.use(timeout(5000));
app.use(haltOnTimedout);

function haltOnTimedout(req, res, next){
   if (!req.timedout) next();
   else console.log('REQUEST TIMEOUT');
}

app.use(myBigMiddleware);   // this will call 3rd party service and can time-out
app.use(myAfterwardsMiddleware);
[maybe more post-processing middleware]

会给我以下输出:

events.js:176
    domain.enter();
       ^

TypeError: domain.enter is not a function
    at IncomingMessage.emit (events.js:176:12)
    at Timeout._onTimeout     (/home/user/dev/node_modules/connect-    timeout/index.js:43:11)
    at tryOnTimeout (timers.js:232:11)
    at Timer.listOnTimeout (timers.js:202:5)
*app died*

当我将app.use(haltOnTimedout); 放在中间件链的最末端时,我会得到相同的结果。

我做错了什么?或者我应该在哪里/如何插入超时中间件以捕获超时并发送我的 HTTP 响应,取消所有后续中间件? :/


[编辑] 我发现app.use(myBigMiddleware); 可能会导致这种情况,因为当我用普通的app.use(()=>{}); 替换它时,我会在控制台和浏览器中得到以下信息(应用仍在运行):

ServiceUnavailableError: Response timeout
    at IncomingMessage.<anonymous> (/home/user/dev/node_modules/connect-timeout/index.js:70:8)
    at emitOne (events.js:96:13)
    at IncomingMessage.emit (events.js:188:7)
    at Timeout._onTimeout (/home/user/dev/node_modules/connect-timeout/index.js:43:11)
    at tryOnTimeout (timers.js:224:11)
    at Timer.listOnTimeout (timers.js:198:5)

这有点接近我的需要,实际上我现在可以将以下中间件添加到堆栈的末尾以实现我想要的:

app.use(function(err, req, res, next) {
    if (req.timedout) {
        res.status(503).end();
    } else {
        res.status(err.status || 500).end();
    }
});

但这让我无法使用我的中间件。有谁知道上面的TypeError: domain.enter is not a function 可能是什么原因造成的? :(


解决方案

原来我在 timeout-middleware 之后有几个额外的中间件,这导致了这个错误。我没有找到根本原因,但是我最终添加了这个中间件:

    function timeout(ms) {
        let id = null;
        return function(req, res, next) {
            id = setTimeout(() => { if (!res.headersSent) res.status(503).end(); }, ms);

            res.on('finish', () => { clearTimeout(id); });
            next();
        };
    };

这实际上是我最初想要的,不管有多少中间件被链接起来。

【问题讨论】:

    标签: node.js express request timeout connect


    【解决方案1】:

    你可以使用async.race:

    app.get('/whatever', (req, res) => {
        async.race([
            (callback) => {
                yourRealFunction(() => {
                    callback(null, 'OK');
                });
            },
            (callback) => {
                setTimeout(() => {
                    callback(null, 'Timed out');
                }, 5000);
            }
        ], (error, result) => {
            if(result === 'Timed out') {
                res.status(503).end();
            } else {
                res.status(200).end();
            }
        });
    });
    

    当然,您可以将它放在中间件中,或者放在它自己的函数中以防止代码重复。

    【讨论】:

    • 我有一个相当复杂的路由器和中间件链,所以我不知道如何插入它。但是我找到了一个类似于你的解决方案(我将它添加到原始帖子中)。
    猜你喜欢
    • 1970-01-01
    • 2019-10-26
    • 2011-09-28
    • 2011-12-02
    • 2020-02-18
    • 1970-01-01
    • 2021-04-28
    • 2014-04-25
    • 1970-01-01
    相关资源
    最近更新 更多