PHP排队问题 - 冗余进程

时间:2012-10-29 21:56:39

标签: php queue

有一个问题,我的一个团队成员正在PHP中进行排队过程。 PHP脚本在命令行上运行,并在每个循环后递归调用自身,以检查数据库中是否有任何项目等待处理。如果有,它会自行处理,处理队列中的项目,并重复。如果没有,它就会死掉,而cron作业每5分钟重新启动一次队列。

偶尔会有两个进程同时运行,抓取相同的队列,并以彼此的方式进入。我正在考虑在唤醒过程中引入一个抖动,以便在进程启动之间有一段随机的时间。

有更好的方法吗?

3 个答案:

答案 0 :(得分:4)

抖动只会使碰撞更难以预测。

将互斥锁放在磁盘上。检查它是否超过两个周期之前。如果是,请忽略并删除它;它被坠毁的东西遗忘了。否则,已有一个已经开始运行,所以保释。

答案 1 :(得分:2)

如果不必执行队列中的项目,您可以执行类似的操作,只需让两个进程并行运行。

从队列中选择下一个作业(显然语法将依赖于数据库)

LOCK TABLE queue;

SELECT * FROM queue WHERE status <> 'INPROGRESS' ORDER BY id LIMIT 1

UPDATE queue SET status = 'INPROGRESS' WHERE id = ?

UNLOCK TABLES;

然后将作业标记为已完成或从队列中删除

如果你想找到陷入困境的工作,你可以存储一个工作开始的时间戳,如果它在那里超过x分钟就把它放回队列

答案 2 :(得分:0)

LOCK TABLE queue;有点矫枉过正,因为它看起来像整个表,所以根本就没有并发性。如果使用字段更新方法(状态更新为INPROGRESS),则根本不需要锁定。 SQL更新操作在内部执行锁定,因此类似这样的操作必须同时工作:

UPDATE queue SET consumer_id='a_consumer_unique_id' status='INPROGRESS' LIMIT 1

SELECT * FROM queue WHERE consumer_id='a_consumer_unique_id' LIMIT 1

但这种方法有副作用。我们必须更改标志INPROGRESS或设置consumer_id。如果消费者偷看了一条消息(对其设置了consumer_id)并消失了消息会发生什么?它会长时间排队,所以你需要一种清洁脚本来解决这些问题。

我们可以做的是将邮件标记为我们的邮件,加载到内存,然后将其从队列中删除。只有在我们完成它之后,我们才开始处理它。 enqueue/dbal就是这样做的。

如果您需要重新发送保证,请使用像RabbitMQ这样的真正经纪人。