请教大家在 NodeJS 里如何处理比较耗时的任务

37 天前
 subtleworks

大家好

我用 Express 搭建了一个服务器 有的请求会处理一些非常耗时的任务 可能会持续几分钟到几十分钟不等

通常会先返回给前端告知任务正在处理 然后会在请求里调用处理这个任务的方法

我发现耗时短的任务会很快执行完 但耗时长的就会中断进行 并且如果同时还有其他的任务进来 就都会中断执行

我对 Express 和 Node.js 的认知都很浅 但描述给 GPT 也是答非所问 所以请教大家如何解决这个问题

谢谢

2086 次点击
所在节点    Node.js
32 条回复
newlifeinsc
37 天前
再额外部署一个服务来处理耗时任务,这个服务就不要接收外部请求了,通过队列来沟通。

比如你现有项目部署了服务 A ,然后你再把这个项目部署一个服务 B 。A 接收到 http 请求要处理耗时任务,那你通知 B 处理就行了。至于 A 通知 B ,可以用队列。如果你没有队列的服务,那也可以直接从 A 发请求给 B ,通知 B 处理。
newlifeinsc
37 天前
至于你文中写的 "耗时长的就会中断进行 并且如果同时还有其他的任务进来 就都会中断执行", 这句意义不明,你这里中断执行是啥意思?

本身你在执行耗时任务时,程序还是可以接收外部请求的,你难道是期望他不处理这些请求吗?
bianhui
37 天前
单线程异步模型啊,你理解为所有操作都会到异步队列里面,所以请求来了,还是你的后台任务的异步操作都可能会乱序,你也不要指望按照期望的顺序执行。
最好的办法,部署一个 job 系统,前台接收到任务,通过,消息、信号或其他方式像 job 发送执行请求。
subtleworks
37 天前
@newlifeinsc 就是如果此时又来一个任务 B 那么原先的任务 A 好像也会被打断 疑似有这个现象
subtleworks
37 天前
@newlifeinsc 感谢回复 这个服务是那种定时的 cron job 吗 还是两码事
subtleworks
37 天前
@bianhui 感谢回复 如果我想要系统地理解相关的知识 有什么关键词我可以搜索吗?
newlifeinsc
37 天前
@subtleworks 任务 A 被打断,说明是它自己本身被打断,一般情况都是在等待 io ,你有没有任务进来实际都在中断后等待。新进来任务 B,如果和任务 A 本身没有业务关系,你就不应该管它。如果你要强制后来的任务 B 一定得在任务 A 完成后才执行,那你就得通过队列来控制。
ferock
37 天前
可能被服务器 kill 了…云服务器都有这个机制
XCFOX
37 天前
subtleworks
37 天前
@newlifeinsc 好的 谢谢解释
subtleworks
37 天前
@ferock 也有可能 不过我也不知道哈哈
subtleworks
37 天前
@XCFOX 谢谢分享链接!
wangtian2020
36 天前
任何一个名字里带“Sync”的 nodejs 自身的 API 都不要使用
比如当你想使用 fs 的读取文件时不要使用
```
let fs = require('fs')
fs.readFileSync
```
而是使用
```
let fs = require('fs')
;(async () => {
await fs.promises.readFile
})()
```
这样子就不会造成阻塞。你 express 的方法参数全部采用 async function 然后用 promise 风格就不会造成阻塞。

如果一个操作会耗时几分钟的话,那么接口就立即返回,告诉前端“我在做了”,然后让前端每个几秒轮询 express ,问后端“有没有做完呀”做完就拿结果没做完继续等。
wangtian2020
36 天前
记住,nodejs 里只有一种情况必须阻塞卡死,就是真的在计算,比如以下代码会卡个几秒钟
```
let i = 4000000000
while (i--) {}
console.log('我循环完了 4000000000 次');
```
其他所有情况,被阻塞住不能处理新进的请求都是错误写法导致的!
subtleworks
36 天前
@wangtian2020 好的 谢谢举例
lzgshsj
36 天前
bullmq
foam
36 天前
13 楼正解。同步方法会停止事件循环,直到操作完成。所以作为一个应用,任意 io 操作(包括网络,文件读写)都必须用异步方式。
lisongeee
36 天前
请问这个耗时的任务具体是什么,能说得更清楚吗?
subtleworks
36 天前
@lisongeee 大致可以理解为是不同格式之间的转换
lisongeee
36 天前
我就假设你的任务是将一个大的二进制数据转换到另一个大的二进制数据,有两种方法改进

- 用 worker ,类似 nodejs 在其它语言的子线程,这样你的主线程就不受到干扰
- 将你的处理函数改成 async 的,内部将大任务按数据量分成批量小任务,每个小任务用 await new Promise(r=>setTimeout(r)) 隔开,这样你的大任务就不会同步阻塞

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/1031152

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX