如何停止集群中调度程序作业的重复执行

问题描述

我们有一个使用预定作业的节点应用程序,我的预定作业一天工作 6 次。 现在我们要将节点应用程序升级到基于集群的,我的意思是主节点和工作节点。 通过这样做,我看到作业被重复并执行了多次。

  1. 我必须删除重复执行
  2. 如果 master 无法运行作业或 master 应用程序停止,worker 应执行 工作

注意:我们使用sql server作为数据库

解决方法

第一

SQL Server 是实现这一点的最糟糕的软件,尤其是在它非常动态的情况下。原因是像 MEMORY 和 MyISAM 这样的引擎只有全表锁,而像 InnoDB 这样更合适的引擎有更高的写惩罚(提供 ACID 属性)并且针对访问空间和时间上接近的记录进行了优化,但事实并非如此使用您提供的场景。

SQL SERVER 的解决方案

但是,如果您坚持使用 SQL 服务器。这是解决方案-

在关系数据库系统中实现作业队列的最佳方法是使用 SKIP LOCK。调度可以归类为图问题,其中 SQL 表中的每个事务都可以视为一个节点,并且您只能在 DAG fashion 中访问这些节点一次。要解决此问题,您需要在作业队列架构中使用 SKIP LOCKS

QueueMsgId identity -- NOT NULL
QueueMsgType varchar(20) -- NOT NULL  
QueueState char(1) -- 'N' New if queued,'A' Active if processing,'C' Completed,default 'N' -- NOT NULL 
CreateTime datetime -- default GETDATE() -- NOT NULL  
QueueMsg varchar(255) -- NULLable 

QueueClient 拉取一条消息并将状态更改为活动,同时处理它。完成后,它会将状态更改为完成。 SKIP LOCKED 是一种锁定机制,它获取对读/共享 (FOR SHARE) 或写/独占 (FOR UPDATE) 的锁。

如果我们有多个并发用户试图访问队列记录,那么我们要确保在我们读取数据后没有人可以更改数据,我们可以使用repeatable read。通过使用它,我们所做的任何表的任何读取都将被锁定以进行更新或删除。执行以下 SELECT 查询,这些查询以独占方式锁定队列记录,同时还为并发添加 SKIP LOCKED 选项:

SELECT
    q.QueueMsgId AS id1,q.QueueMsgType AS msgType1,q.QueueState AS state1,FROM
    Queue q
WHERE
    q.QueueState = 'A'
ORDER BY
    q.QueueMsgId
LIMIT 2
FOR UPDATE OF q SKIP LOCKED

对于多个用户,执行相同的查询,队列记录不会有冲突

最佳解决方案

DUMP SQL 并转向 REDIS 以获得高查询每秒吞吐量。虽然 Redis 的数据结构是并发的,但很难处理。幸运的是,我们在 node.js 中为 Job/Message Queue 系统提供了开箱即用的解决方案 - BULL,用于基于集群的部署检查 this example