【问题标题】:PHP Queuing Issue - Redundant processesPHP 排队问题 - 冗余进程
【发布时间】:2012-10-19 06:37:37
【问题描述】:

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

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

有没有更好的办法?

【问题讨论】:

    标签: php queue


    【解决方案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 分钟,则将它放回队列中

      【讨论】:

        【解决方案3】:

        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 等真正的代理。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2021-05-11
          • 1970-01-01
          • 1970-01-01
          • 2013-05-03
          • 2021-11-17
          相关资源
          最近更新 更多