我正在我的服务中构建一个类似 CRON 的模块(使用node-schedule),我的多核设置的每个实例都需要该模块,我想知道它们都在运行自己的线程,并且它们是所有计划同时运行,它们会被每个线程调用还是只调用一次,因为它们都加载相同的模块。
如果它们确实被多次调用,那么确保所需操作仅被调用一次的最佳方法是什么?
如果您使用 pm2 进行集群模式,则可以使用 process.env.NODE_APP_INSTANCE 检测哪个实例正在运行。您可以使用以下代码,这样您的 cron 作业将仅被调用一次。
// run cron jobs only for first instance
if(process.env.NODE_APP_INSTANCE === '0'){
// cron jobs
}
node-schedule
在给定的节点进程内运行,并安排该特定节点进程要求其安排的事情。
如果您正在运行多个节点进程并且每个进程都使用节点调度,那么这些单独的节点进程中的所有节点调度实例都是独立的(它们之间没有合作或协调)。 如果每个节点进程要求其自己的节点计划实例在该月第一个星期三下午 3 点运行特定任务,那么所有节点进程都将在那时开始运行该任务。
如果您只想执行一次操作,那么您必须在节点实例之间进行协调,以便该操作仅在一个节点进程中调度,而不是在所有节点进程中,或者仅在其中一个节点中调度这些类型的操作实例,不是全部。
以通用方式处理此问题的最佳方法是拥有一个向其写入“锁定”条目的共享数据库。例如,假设所有任务都写入了一个数据库条目,例如
{instanceId: "a", taskId: "myTask", timestamp: "2021-12-22:10:35"}
。
所有任务都会提交相同的内容,除了它们自己的instanceId。然后,您在“时间戳”上有一个唯一索引,因此只有 1 个被接受。
然后他们都进行查询,看看他们的节点是否是被接受执行 cron 的节点。
您可以做同样的事情,但还可以添加一个“随机”字段来生成随机数,并且数字最小的任务获胜。
如果您想只运行一次 cron 作业, 您可以在代码中像这样使用 redis 。 这样 cron 作业只运行一次:
const lockKey = 'cron-job-lock';
const lock = await this.redis.client.sendCommand([
'SET',
lockKey,
'locked',
'NX',
'EX',
'86000', // lock time
]);
if (lock) {
console.log('Running cron job');
// do your task here
} else {
console.log('Cron job already handled by another instance');
}