Node.js 快速-将请求限制为一次一个

Node.js 快速-将请求限制为一次一个,node.js,express,rate-limiting,Node.js,Express,Rate Limiting,我正在使用它来构建一个应该在内部使用的API。其中一个请求会在服务器上触发一个繁重的进程,并应返回一个CSV。此过程可能需要10分钟以上 为了不使服务器过载,我想限制这个API的调用,这样,由于进程没有终止,我们就不能再次请求相同的URL 为此,我尝试使用以下配置: new RateLimit({ windowMs: 30 * 60 * 1000, // 30 minutes max: 1, delayMs: 0, // disabled message: 'Th

我正在使用它来构建一个应该在内部使用的API。其中一个请求会在服务器上触发一个繁重的进程,并应返回一个CSV。此过程可能需要10分钟以上

为了不使服务器过载,我想限制这个API的调用,这样,由于进程没有终止,我们就不能再次请求相同的URL

为此,我尝试使用以下配置:

new RateLimit({
    windowMs: 30 * 60 * 1000, // 30 minutes
    max: 1,
    delayMs: 0, // disabled
    message: 'Their is already a running execution of the request. You must wait for it to be finished before starting a new one.',
    handler: function handler(req, res) {
        logger.log('max request achieved');
        logger.log(res);
    },
});
但似乎每次都在2分钟后达到“最大请求”,即使我只启动一次。我怀疑浏览器会在2分钟后重试请求,如果没有得到任何答复,是否可能

我希望此请求没有任何重试策略,并且达到最大请求的唯一方法是手动请求服务器连续执行此请求2次

谢谢

编辑 这是我的全部代码:

const app = express();
const port = process.env.API_PORT || 3000;

app.enable('trust proxy');

function haltOnTimedout(req, res, next) {
    if (!req.timedout) { next(); }
}

app.use(timeout(30 * 60 * 1000)); // 30min
app.use(haltOnTimedout);

app.listen(port, () => {
    logger.log(`Express server listening on port ${port}`);
});

// BILLING
const billingApiLimiter = new RateLimit({
    windowMs: 30 * 60 * 1000, // 30 minutes
    max: 1,
    delayMs: 0, // disabled
    message: 'Their is already a running execution of the request. You must wait for it to be finished before starting a new one.',
    handler: function handler(req, res) {
        logger.log('max request achieved');
    },
});

app.use('/billing', billingApiLimiter);
app.use('/billing', BillingController);
我的路线代码是:


我找到了这个GitHub问题的答案:

TLDR:我添加了request.connection.setTimeout1000*60*30;避免每2分钟触发一次请求


但考虑到我在问题中所写的代码,@Paul的建议仍然值得考虑。

我觉得这是一个糟糕的设计。速率限制通常更多地是为了遵守定价计划或防止DOS攻击,而不是考虑长时间运行的流程。您是否考虑过使用作业队列或类似工具,以确保您的后端一次只获得一个作业,而不是使用速率限制工具?@Paul是对的,有工人的队列可能会更好地解决这一问题。如果您确实想这样做,那么我可能会建议您在所需的端点中添加一些代码,检查流程是否已经运行,如果已经运行,则返回一个错误。IE:您可以存储一个进程正在db或redis中运行的事实,首先检查它,然后在进程完成时释放锁。这并不是一个非常依赖的方法,但如果整个过程相当简单,它可能会实现你想要的。谢谢@Paul和@Elliot!设置锁是我的第一个想法,如果锁没有释放,我会终止请求,但它总是在2分钟后收到第二个呼叫,然后向客户端返回错误。我认为这是由于一些“重试策略”,但我真的不是专家。如果我使用JobQueue,这将避免服务器过载,但它仍然是每2分钟执行一次请求的问题。我试图用连接超时设置超时,但可能我做得不对,因为它没有改变任何东西。我在问题中添加了一些额外的代码。生成csv需要多长时间?
router.get('/billableElements', async (request, response) => {
    logger.log('Route [billableElements] called');
    const { startDate } = request.query;
    const { endDate } = request.query;
    try {
        const configDoc = await metadataBucket.getAsync(process.env.BILLING_CONFIG_FILE || 'CONFIG_BILLING');
        const billableElements = await getBillableElements(startDate, endDate, configDoc.value);
        const csv = await produceCSV(billableElements);
        logger.log('csv produced');
        response.status(200).send(`${csv}`);
    } catch (err) {
        logger.error('An error occured while getting billable elements.', err);
        response.status(500).send('An internal error occured.');
    }
});