我有一个 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 发送到一个单独的单线程,一个一个地排队和处理论文。
有什么建议吗?
最佳答案
问题
问题不在于 doWork()
花费了很多时间,而在于它显然会在整个生命周期内阻塞您的线程,并且不会让事件循环有任何运行的机会。
可能的原因
这可能是由多种原因引起的,我在这里只能猜测,因为您没有显示 doWork()
的来源,甚至没有描述它的作用和方式。例如:
- 您的
doWork()
可能会使用诸如fs.readFileSync()
之类的阻塞操作或名称中带有Sync
的其他函数。 - 您的
doWork()
可能有一个for
或while
循环,它会旋转 5-10 分钟并在这样做时阻塞事件循环. - 您的代码进行了一些严肃的数字运算,没有分成多个步骤让事件循环在这些步骤之间滚动。
一般来说,如果您的 doWork()
不阻塞您的主线程,并且它不应该阻止其他连接获得服务,即使是一毫秒,它也可能需要数小时才能运行。
解决方案
停止阻塞线程
该问题最简单的解决方案可能是避免阻塞函数调用(那些带有 Sync
后缀或您自己的函数)、长时间运行的循环和没有分成短步骤的繁重计算。
例如:
- 不要使用
readFileSync()
,而是使用readFile()
- 不要使用长时间运行的 for/while 循环,而是使用
process.nextTick()
- 不要使用非常深的递归(可能要归功于 TCO),而是使用通过
process.nextTick()
分成多个部分的循环
如果无法应用上述解决方案(由于我对您的 doWork()
函数一无所知,所以我无从得知)那么您可以采取另一种方法。您还可以做一些其他事情。
产生一个进程
另一种解决方案是在每次启动长时间运行的任务时使用 child_process
生成不同的进程。当 child 结束工作并做出相应 react 时,您的主要流程可能会收到通知,但在等待时不会被阻止。请参阅:https://nodejs.org/api/child_process.html
使用队列
您还可以使用待处理作业队列并由其他进程处理它们,而不会影响您的主程序,该程序只会安排新任务而不执行或等待它们。通常像这样的队列是用 Redis 完成的,但也可以用 CouchDB 或 MongoDB 完成。您需要有一些挂起任务的中央注册表,您的工作进程可以从中获取它们。在 Node 中有很多模块可以做到这一点,例如:
- http://automattic.github.io/kue/
- https://www.npmjs.com/package/bull
- https://www.npmjs.com/package/bee-queue
- https://www.npmjs.com/package/node-taskman
- https://www.npmjs.com/package/cluster-master
- https://www.npmjs.com/package/agenda
- https://www.npmjs.com/package/worker-farm
请参阅这些模块的文档以了解哪一个最适合您的需求。
关于node.js - 在单独的线程 Node Js 中运行长时间运行的快速 API 进程,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40085654/