我需要一个具有多个任务的Job,在不同的机器上运行,一个接一个(不是同时),并且在当前作业运行时,另一个相同的作业可以到达队列,但是不应该启动前一个已经完成。所以我提出了这个“解决方案”,这可能不是最好的,但它完成了工作:)。我只有一个问题。
我发现我需要一个具有以下结构的JobQueue(MongoDb或Redis):
{
hostname: 'host where to execute the task',
running:FALSE,
task: 'current task number',
tasks:{
[task_id:1, commands:'run these ecommands', hostname:'aaa'],
[task_id:2,commands:'another command', hostname:'bbb']
}
}
主机:
因为作业可以累积,想象一下作业排队等待一个主机的情况:A,B,A
由于我必须为指定的机器运行所有作业,我怎么不启动第3个A(第一个A仍在运行)?
答案 0 :(得分:0)
{
_id : ObjectId("xxxx"), // unique, generated by MongoDB, indexed, sortable
hostname: 'host where to execute the task',
running:FALSE,
task: 'current task number',
tasks:{
[task_id:1, commands:'run these ecommands', hostname:'aaa'],
[task_id:2,commands:'another command', hostname:'bbb']
}
}
问题是下一个可用的"工人"知道在特定主机上开始下一份工作是否安全。
您可能需要某种可排序(索引)字段来指示作业的到达顺序。如果您正在使用MongoDB,那么您可以让它生成_id
,它已经是唯一的,索引的并且按时间顺序,因为它的前四个字节是时间戳。
您现在可以查询是否有针对特定主机运行的作业,如下所示:
// pseudo code - shell syntax, not actual code
var jobToRun = db.queue.findOne({hostname:<myHostName>},{},{sort:{_id:1}});
if (jobToRun.running == FALSE) {
myJob = db.queue.findAndModify({query:{_id:jobToRun._id, running:FALSE},update:{$set:{running:TRUE}}});
if (myJob == null) print("Someone else already grabbed it");
else {
/* now we know that we updated this and we can run it */
}
} else { /* sleep and try again */ }
这样做是为特定主机检查最早/最早的工作。然后它会查看该作业是否正在运行。如果是,那么什么也不做(睡觉再试一次?)否则试着&#34;锁定&#34;通过在_id
上执行findAndModify并运行FALSE并将运行设置为TRUE来实现它。如果返回该文档,则表示此过程成功完成更新,现在可以开始工作。由于两个线程可以同时尝试执行此操作,如果您返回null
,则表示此文档已更改为由另一个线程运行,我们等待并重新开始。
我建议在某处使用时间戳来指示作业何时开始&#34;运行&#34;因此,如果一个工人在没有完成任务的情况下去世,那么就可以找到&#34; - 否则它会阻塞&#34;同一主机背后的所有工作。
我所描述的是一个队列,你可以在完成时删除作业,而不是将运行设置为FALSE - 如果你将运行设置为FALSE以便其他&#34;任务&#34;可以完成,然后您可能还会更新任务数组以指示已完成的操作。