6

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

有时,两个进程同时运行,抢占同一个队列,并相互干扰。我正在考虑在唤醒过程中引入抖动,以便在过程开始之间有一个随机的时间量。

有没有更好的办法?

4

3 回答 3

4

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

在磁盘上放置一个互斥锁。检查它是否早于两个周期前。如果是,忽略并删除它;它被坠毁的东西抛在了后面。否则,已经有一个在运行,所以保释。

于 2012-10-29T21:57:55.527 回答
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 分钟,则将它放回队列中

于 2012-10-29T22:04:03.560 回答
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 等真正的代理。

于 2017-06-22T10:00:30.283 回答