使用MySQL作为作业队列

3
我希望将MySQL用作作业队列。多台机器将生产和消费作业。作业需要进行调度;有些作业每小时运行一次,有些作业每天运行一次等等。
这似乎相当简单:对于每个作业,有一个“nextFireTime”列,并且工作机器会查找具有nextFireTime的作业,将记录的状态更改为“inProcess”,然后在作业结束时更新nextFireTime。
问题在于当工作机器默默死亡时。它将无法更新nextFireTime或将状态设置回“idle”。
不幸的是,作业可能运行时间很长,因此无法使用查找已经处理了太长时间的作业的收割线程。没有适合的超时值。
有人能否建议一种设计模式,以正确处理不可靠的工作机器?

3
有难度。长时间运行的作业是否需要定期更新一个“仍处于状态”的列,并要求每 X 分钟更新一次?然后再让拾荒者说:“如果您在 X 分钟内没有进行更新,那么将终止!” - Marvo
或许更好的设计是让作业队列自己查询作业以确定它们的状态(一种监听器模式)。作业必须知道如何响应状态查询。 - Marvo
是的,我考虑过这个。有点像心跳信号。工业控制器就是这样做的。这是可能的,但这意味着所有的作业处理器都必须有某种内部循环来进行更新。这不是一个理想的解决方案。 - ccleve
已经有像RabbitMQ这样的成熟任务队列服务器了,为什么不使用其中之一,而要重新发明轮子呢? - Nick Johnson
这不是对你问题的回答,但请阅读这篇文章:http://www.engineyard.com/blog/2011/5-subtle-ways-youre-using-mysql-as-a-queue-and-why-itll-bite-you/ - toong
链接失效了,现在博客文章在 https://www.engineyard.com/blog/5-subtle-ways-youre-using-mysql-as-a-queue-and-why-itll-bite-you - gimpf
3个回答

4
也许是这样的:
当一个工作进程获取一个任务时,它可以将其进程ID或其他唯一ID添加到任务的一个字段中。
然后,在另一个表中,每个工作进程都会更新一个值以表示它们还活着。在更新“我还活着”字段时,您需要检查所有其他“最后一次工作进程发出生命迹象”的字段。如果有一个工作进程超过了限制时间,则需要找到它正在处理的所有任务并将它们重置。
换句话说,看门狗程序是针对工作进程而不是任务本身运行的。

4
使用MySQL作业队列通常会带来痛苦,因为它与关系型数据库管理系统的一般目标非常不匹配。用户'toong'已经链接到https://www.engineyard.com/blog/5-subtle-ways-youre-using-mysql-as-a-queue-and-why-itll-bite-you,其中有很多有趣的内容。不可靠的工作者只是其中一个问题。
处理作业分发的系统有很多,大多数都通过其排队和调度功能的复杂性而区分。在简单的FIFO端口,例如Resque、Celery、Beanstalkd和Gearman;在复杂的端口上,例如GridEngine、Torque/Maui和PBS Pro。如果您可以容忍依赖于亚马逊服务(我相信它不需要您在EC2中),我强烈推荐新的亚马逊简单工作流系统。

针对你最初的问题:目前我们正在实施一个每个节点监督程序,可以判断节点的作业是否仍在运行,并向作业监视器发送心跳信号。这是一件麻烦的事情,但正如你正在发现和将继续发现的那样,有很多细节和错误情况需要管理。然而,我必须鼓励你学习这个领域并从一开始就正确地构建系统,这样会对你自己有好处。


1
一种选择是确保作业具有幂等性,并允许多个工作者启动给定的作业。不管哪个工作者完成作业,或者是否有多个工作者完成作业;由于作业被设计成能够优雅地处理多个完成情况,因此这并不重要。也许工作者们会竞速提供结果,而失败者发现将保存结果的插槽已经满了,所以他们只好丢弃它们。
另一个选择是不要有大型作业。如果作业运行时间超过(比如)1分钟,请将长时间运行的作业分解为中间步骤,将中间结果存储为新作业(以某种方式链接到旧作业),以便可以将新作业重新排队以执行另外一分钟的工作。

是的,那是可能的。重叠的工作可能没问题。 - ccleve

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接