如何避免多个节点进程做重复的事情?

时间:2017-01-10 06:31:44

标签: javascript node.js mongodb

我在Node.js中有一个模块,它重复从MongoDB中选择一个文档并对其进行处理。一份文件只应处理一次。我也想使用多个流程概念。我想在不同的处理器上运行相同的模块(进程),这些处理器是独立运行的。

问题是,可能存在两个不同工作人员挑选和处理同一文档的情况。多个进程如何知道某个特定文档是由其他工作者处理的,所以我不应该触及它。我的独立流程无法与之沟通。我不能使用一个父进程来处理多个进程并充当它们之间的桥梁。如何在Node.js中避免这种问题?

2 个答案:

答案 0 :(得分:2)

一种方法是为每个MongoDB文档分配一个唯一的数字ID,并为每个node.js工作者分配一个唯一的数字标识符。

例如,有一个名为NUM_WORKERS的env var,然后在你的node.js模块中:

var NumWorkers = process.env.NUM_WORKERS || 1;

然后,您需要为每个工作人员分配一个唯一的,连续的实例编号id(范围为0到NumWorkers-1)(例如,通过node.js进程初始化时读取的命令行参数)。您可以将其存储在名为MyWorkerInstanceNum的变量中。

当您从MongoDB中选择文档时,请调用以下函数(将文档的唯一documentId作为参数传递):

function isMine(documentId){
    //
    // Example: documentId=10
    //          NumWorkers= 4
    // (10 % 4) = 2
    // If MyWorkerInstanceNum is 2, return true, else return false.
    return ((documentId % NumWorkers) === MyWorkerInstanceNum);
}

如果isMine()返回true,则仅继续实际处理文档。 因此,多个工人可能会选择"一个文档,但只有一个工人会实际处理它。

答案 1 :(得分:1)

只需通过其唯一ID保留正在处理的文档的事务日志。在已处理文档的事务日志表中,将状态写为以下之一(例如):

requested
initiated
processed
failed

您可能还希望该表中的列为stderr / stdout,以防您想知道失败或成功的原因,以及时间戳 - 这类事情。

在Node应用程序中初始化文档处理时,请按ID查找文档并检查其状态。如果它不存在,那么你可以自由地处理它。

伪代码(对不起,我不是蒙古人!):

db.collection.list('collectionName', function(err, doc) {
    db.collection.find(doc.id, 'transactions', function(err, trx) {
        if (trx === undefined || trx.status === 'failed') {
            DocProcessor.child.process(doc)
        } else {
            // don't need to process it, it's already been done
        }
    })
})

您还需要在事务日志集合上启用并发锁定,以确保无法复制行(和后续作业)。如果这成为确保文档正确排队的挑战,请考虑添加AMQP服务来处理文档的排队。设置处理程序以管理子进程和事务日志记录的分发。流程将类似于:

MQ⇢日志⇢处理程序⇢Doc处理器子项