我有一个Node.js express API,托管在Azure App Service上,从SQL Server数据库提供数据。新要求是根据可自定义的数据导出报告。这些应该是动态生成的CSV文件。
我面临的问题是CSV文件在某些情况下会非常大(可能是500,000行)。我不难创建这些文件,然后可能将它们上传到Blob存储,或者直接将它们提供给用户。但是,正如您可以想象的那样,这会对服务器产生一些负担。此API每分钟提供约500个发布/获取请求。 Node的单线程特性意味着当有人请求像这样的CSV文件时,服务器会被抓取。
我需要一些关于做什么的建议。我的想法是在一个新服务器(可能是用ASP.NET编写的一个服务器?)上启动一个新的应用程序,它负责创建CSV文件。它公开了一个端点,它接收创建文件的参数,然后用文件或链接进行响应。但是因为它是一个单独的服务器(如果我做了像.NET那样的单独的线程),它在等待响应时不会减慢Node服务器的速度。然而,这似乎做了很多工作。有什么比这更简单的了吗?我对任何可以直接连接到SQL数据库并生成报告的服务持开放态度(因此我可以将其提供给客户端,而不是我需要硬编码的定制报告)。
答案 0 :(得分:0)
您遇到的问题与CSV文件操作无关。 它正在处理巨大的任务。因为node.js是单线程的,所以一个巨大的任务可以"阻止" 服务器。
解决方案是将大量任务减少为最小的任务。
您可以使用setTimeout()
函数执行此操作。
例证:
不减少任务(任务2和3将等到任务1完成)
[ Task 1 ][Task 2][ Task 3 ]
使用setTimeout()
[ Task 1.0 ][ Task 1.1 ][Task 2][ Task 1.2 ][ Task 3 ][ Task 1.3 ][ Task 1.4 ]
任务不会被阻止
答案 1 :(得分:0)
(我写道,这并没有意识到你正在使用Azure App Service。我不确定Azure App Service是否允许使用child_process.fork。如果没有,这对你来说是错误的答案,尽管它可能会帮助不同的人平台。)
另一个解决方案是使用child_process.fork
在另一个进程中执行繁重的任务:
// worker.js
process.on('message', msg => {
process.send(makeCSV(msg));
});
// server.js
const cp = require('child_process');
app.get('/', (req, res) => {
const child = cp.fork('./worker.js');
child.on('message', csv => {
res.send(csv);
});
child.send(req.params);
});
分叉流程相当繁重。如果您希望处理大量CSV请求,那么您可以在应用首次启动时创建一个工作池,而不是为每个请求启动新流程。谷歌搜索引导我到https://github.com/thisandagain/fork-pool,它似乎为你处理游泳池。
如果这些昂贵的CSV请求相对较少,https://nodejs.org/api/cluster.html#cluster_cluster也可能会解决您的问题。这将启动多个服务器进程,因此如果一个或两个忙于制作CSV文件,您仍然可以让其他人自由处理其他请求流。
答案 2 :(得分:0)
如果您无法使用其他流程,则可以编写CSV生成代码,以便使用setImmediate
来计划数据生成。然后它将间歇性地产生,以便线程可以处理其他请求。
const chunkSize = 1000;
function generateCSV(rows, cb) {
const csv = [];
function handleChunk(rows, rest) {
for (let row of rows) {
csv.push(`${rows[i].field1},${rows[i].field2}`);
}
if (rest.length) {
setImmediate(handleChunk, rest.slice(0, chunkSize), rest.slice(chunkSize));
} else {
cb(null, csv.join('\n'));
}
}
handleChunk(rows.slice(0, chunkSize), rows.slice(chunkSize));
}