我有一个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发送到一个单独的单线程,该线程逐个排队和处理这些。
有什么建议吗?
答案 0 :(得分:13)
问题不在于doWork()
需要花费很多时间,但它显然会在整个生命周期内阻塞你的线程,并且不会让事件循环有任何机会运行。
这可能是由几件事造成的,我只能在这里猜测,因为你没有展示doWork()
的来源,甚至没有描述它的作用和方式。例如:
doWork()
可能会使用fs.readFileSync()
等阻止操作或名称中包含Sync
的其他功能。doWork()
可能会有一个for
或while
循环,旋转5-10分钟并阻止事件循环。通常情况下,如果doWork()
没有阻止主线程,则Sync
可能需要数小时才能运行,并且即使在毫秒内也不会阻止其他连接获得服务。
该问题的最简单的解决方案可能是避免阻塞函数调用(具有readFileSync()
后缀或您自己的函数的调用),长时间运行的循环和繁重的计算,而不是分成短步骤。
例如:
readFile()
使用process.nextTick()
process.nextTick()
doWork()
如果无法应用上述解决方案(由于我对您的child_process
功能一无所知,我无法说出来),那么您可以采取另一种方法。还有一些你可以做的事情。
另一种解决方案是每次启动长时间运行的任务时使用web.config
生成不同的进程。当孩子结束工作并做出相应反应时,可以通知您的主要流程,但在等待时不会被阻止。请参阅:https://nodejs.org/api/child_process.html
您还可以使用待处理作业队列并通过其他进程处理它们,而不影响主程序,该程序仅安排新任务而不执行或等待它们。通常这样的队列是用Redis完成的,但也可以用CouchDB或MongoDB完成。您需要有一些待处理任务的中央注册表,您的工作进程可以从这些任务中获取它们。 Node中有许多模块可以做到这一点,例如:
请参阅这些模块的文档,了解哪种模块最适合您的需求。