在单独的线程NodeJ中运行长时间运行的快速API进程

时间:2016-10-17 11:50:20

标签: node.js multithreading express queue

我有一个API调用大约需要5到10分钟来处理。我在它周围设置了一个超时方法,以便我得到一个状态为排队的即时API响应。

以下简单的视觉

doWork(object) => { /*... Takes 5 minutes */ }

app.post('/longProcess',(req,res)=> {
    setTimeout(this.doWork(req.body), 1000);
    res.send({ status: 'queued' });
})

这适用于第一个立即响应的请求。但第二个请求被锁定等待doWork完成。

而不是使用SetTimeout而不是我真正喜欢做的是将longProcess发送到一个单独的单线程,该线程逐个排队和处理这些。

有什么建议吗?

1 个答案:

答案 0 :(得分:13)

问题

问题不在于doWork()需要花费很多时间,但它显然会在整个生命周期内阻塞你的线程,并且不会让事件循环有任何机会运行。

可能原因

这可能是由几件事造成的,我只能在这里猜测,因为你没有展示doWork()的来源,甚至没有描述它的作用和方式。例如:

  • 您的doWork()可能会使用fs.readFileSync()等阻止操作或名称中包含Sync的其他功能。
  • 您的doWork()可能会有一个forwhile循环,旋转5-10分钟并阻止事件循环。
  • 您的代码执行了一些严重的数字运算,但没有分成让事件循环在这些步骤之间滚动的步骤。

通常情况下,如果doWork()没有阻止主线程,则Sync可能需要数小时才能运行,并且即使在毫秒内也不会阻止其他连接获得服务。

解决方案

停止阻止线程

该问题的最简单的解决方案可能是避免阻塞函数调用(具有readFileSync()后缀或您自己的函数的调用),长时间运行的循环和繁重的计算,而不是分成短步骤。

例如:

  • 而不是使用readFile()使用process.nextTick()
  • 使用process.nextTick()
  • 而不是长时间运行/ while循环
  • 使用循环分为具有doWork()
  • 的部分,而不是非常深的递归(可能归功于TCO)

如果无法应用上述解决方案(由于我对您的child_process功能一无所知,我无法说出来),那么您可以采取另一种方法。还有一些你可以做的事情。

产生一个过程

另一种解决方案是每次启动长时间运行的任务时使用web.config生成不同的进程。当孩子结束工作并做出相应反应时,可以通知您的主要流程,但在等待时不会被阻止。请参阅:https://nodejs.org/api/child_process.html

使用队列

您还可以使用待处理作业队列并通过其他进程处理它们,而不影响主程序,该程序仅安排新任务而不执行或等待它们。通常这样的队列是用Redis完成的,但也可以用CouchDB或MongoDB完成。您需要有一些待处理任务的中央注册表,您的工作进程可以从这些任务中获取它们。 Node中有许多模块可以做到这一点,例如:

请参阅这些模块的文档,了解哪种模块最适合您的需求。