让我先说这个问题,我理解在这个话题上可能不会有一个明确的,是或否的答案,并且给出的答案可能是由意见驱动的。但是,我确实需要并感谢您在部署和操作以下 API 设计时提供的建议和/或指导。
我正在做什么
对于我的 SaaS,我想通过 API 向我的客户提供它的功能。SaaS 提供的任务是一项长期运行且计算成本高昂的任务。因此,不幸的是,运行一个简单的“同步”API,调用者等待这个任务的结果作为对他们请求的响应被传递是不合适的。相反,我选择了一种方法,调用者计划Jobs
并定期查询 API 以查看给定的作业是否已经完成。我对这个设计很满意。
到目前为止我做了什么
对于实施,我建立/使用了以下技术:
Node.js
服务器Express
用于 API 和路由的 npm 包Redis
作业调度数据库bullmq
用于 Redis 连接和队列管理的 npm 包
使用bullmq
,我创建一个Queue
并添加Jobs
到该队列以响应对给定端点的调用。
对我来说,这是api.ts
(缩短):
import Express from "express"
import { Queue } from "bullmq"
const api = Express()
const queue = new Queue("com.mysaas.workerQueue")
api.post("/job", async (request, response) => {
var data, jobId
...
await queue.add("com.mysaas.defaultJob", data, {
jobId: jobId,
removeOnComplete: true,
...
})
response.send({
status: "success",
job: jobId
})
}
...
此外,我创建了处理计划作业的工作人员,由bullmq
包协调,Redis
在引擎盖下使用。
对我来说,这是worker.ts
(缩短):
import { Worker } from "bullmq"
const worker = new Worker("com.mysaas.workerQueue", async (job) => {
await someWork()
...
}, {concurrency: 25})
我目前如何在我的开发环境中运行它
tsc
将 Typescript 编译为 Javascript。
我pm2
用作进程管理器和守护进程。要运行 api,我使用以下命令:
pm2 start -i max build/api/api.js
这将为每个可用的 cpu 内核启动一个节点进程(在我的开发机器上为 8 个)。
要运行工作人员,我打开另一个终端并执行:
node build/worker/worker.js
我可以通过打开更多终端并重复上面的命令来添加工作人员。这样做会导致多个工人Jobs
从 中Queue
分担工作量并同时完成多个工作,从而更快。
所有这些在我的开发环境中都非常有效。
我不确定什么
我不知道的是当前的方法是否适合生产环境。我已经研究了文档pm2
,bullmq
但我似乎无法找到如何将这两者结合起来的明确描述。当然,我的目标是最大化性能和 API 吞吐量。
以下几点仍然是我脑海中悬而未决的问题:
- 实际上会在
pm2
每个可用核心上运行一个进程,即将某个核心分配给给定进程吗?或者它会产生与核心一样多的进程,并且较低级别的系统机制将负载平衡哪个核心的计算时间用于哪个进程。 - 如果是前者,我从我的 API 进程实例启动的子进程是否会分配给父进程正在运行的核心?
- 如上所述启动一个worker,我们可以提供一个
concurrency
选项,它会告诉bullmq 生成尽可能多的进程来运行worker 代码。并发性是否也仅限于分配给主要初始worker 进程的核心(以 开头node build/worker/worker.js
)。
鉴于我对上述几点的不确定性,我提炼了三种在生产环境中运行流程的方法:
启动 API 进程并使用
pm2 start -i max build/api/api.js
. 启动工作进程并使用pm2 start -i max build/worker/worker.js
. 这将使我每个可用核心有 1 个 api 进程和 1 个工作进程。我会假设这种方法可以优化 CPU 负载。但是,我不知道要为工人的concurrency
参数分配什么值。bullmq
将工作代码移动/导入到 API 进程。启动 API 进程并使用
pm2 start -i max build/api/api.js
. 这将给我留下 1 个进程,其中包括 api 和 worker,每个核心。在我看来,这里的缺点是 worker 和 api 是同一个进程,这意味着如果 api 部分导致进程由于某种原因崩溃,worker 会随之关闭。那是对的吗?启动 API 进程并使用
pm2 start -i max build/api/api.js
. 启动并妖魔化具有高参数值 (100+) 的单个工作进程concurrency
。假设并发工作进程被系统负载平衡到可用内核,这也应该会优化 CPU 利用率。不利的一面是,如果单个工作进程崩溃,则在恢复之前不会完成任何工作。
我的直觉告诉我方法 1. 在我的情况下是最好的。但是,这是我第一次将这种 node.js 应用程序部署到生产环境中。因此,我真诚地感谢对所描述项目的任何帮助、建议、指导或反馈。