以非阻塞方式在 http post 请求上写入 excel

Write to excel on http post request in a non-blocking way

我的 Web 服务器上有一个端点可以处理 post 请求,该请求的主体包含对 mongodb 数据库的查询。然后使用 exceljs 一次将查询结果写入一个 excel 文件。当查询产生合理数量的文档时,一旦准备就绪,它就可以很好地响应文件名。当查询结果来自多个请求的 200k+ 文档时,就会出现问题。发生的情况是,通常需要大约 1 分钟才能完成编写和响应单个客户端请求 500k+ 文档的情况,而完成编写和响应多个请求的相同数量或更少文档的查询则需要几分钟。

终点大致是这样的:

app.post('/queryToExcel', async (req, res) => {
    let cursor = mongodb.collection('collection').find(req.body);
    let filename = `./files/excel${Date.now()}`;
    let workbook = new Excel.stream.xlsx.WorkbookWriter({ filename: filename });
    let worksheet = workbook.addWorksheet("Sheet 1");

    let isFirstDoc = true;
    await cursor.forEach(document => {
        if (isFirstDoc) {
            worksheet.columns = getColumnNames(document);
            isFirstDoc = false;
        }
        worksheet.addRow(row).commit();
    });
    
    await workbook.commit();

    res.send(filename);
});

无论 Web 服务器是否正在处理对其他文件的其他请求,我应该如何在与查询请求的文档数量相关的时间范围内做出响应?是线程问题还是我做错了什么?

好吧,如果您使用的库不公开 Async API,那么我建议您使用 worker,否则您只有一个线程。

为了以最有效的方式使用工作人员,我建议您使用队列库 BullMQ

我通过在每个请求上创建一个分叉子进程来解决,如下所示:

端点:

const { fork } = require('child_process');

app.post('/queryToExcel', async (req, res) => {
    const childProcess = fork("./writer.js");
    childProcess.send(req.body);
    childProcess.on('message', filename => {
        res.send(filename);
        childProcess.kill();
    });
});

子进程执行的文件writer.js

const writer = (reqBody) => {
    let cursor = mongodb.collection('collection').find(ReqBody);
    let filename = `./files/excel${Date.now()}`;
    let workbook = new Excel.stream.xlsx.WorkbookWriter({ filename: filename });
    let worksheet = workbook.addWorksheet("Sheet 1");
    let isFirstDoc = true;
    await cursor.forEach(document => {
        if (isFirstDoc) {
            worksheet.columns = getColumnNames(document);
            isFirstDoc = false;
        }
        worksheet.addRow(row).commit();
    });
    
    await workbook.commit();

    process.send(filename);
}