在不降低node.js服务器速度的情况下,即时高效地创建CSV文件报告

时间:2017-09-14 07:59:36

标签: sql-server node.js api csv azure

我有一个Node.js express API,托管在Azure App Service上,从SQL Server数据库提供数据。新要求是根据可自定义的数据导出报告。这些应该是动态生成的CSV文件。

我面临的问题是CSV文件在某些​​情况下会非常大(可能是500,000行)。我不难创建这些文件,然后可能将它们上传到Blob存储,或者直接将它们提供给用户。但是,正如您可以想象的那样,这会对服务器产生一些负担。此API每分钟提供约500个发布/获取请求。 Node的单线程特性意味着当有人请求像这样的CSV文件时,服务器会被抓取。

我需要一些关于做什么的建议。我的想法是在一个新服务器(可能是用ASP.NET编写的一个服务器?)上启动一个新的应用程序,它负责创建CSV文件。它公开了一个端点,它接收创建文件的参数,然后用文件或链接进行响应。但是因为它是一个单独的服务器(如果我做了像.NET那样的单独的线程),它在等待响应时不会减慢Node服务器的速度。然而,这似乎做了很多工作。有什么比这更简单的了吗?我对任何可以直接连接到SQL数据库并生成报告的服务持开放态度(因此我可以将其提供给客户端,而不是我需要硬编码的定制报告)。

3 个答案:

答案 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));
}