创建php兔子工作者的最佳方法

时间:2016-09-07 15:53:37

标签: php rabbitmq infinite-loop worker

假设我有经常填充一些数据的兔子队列(例如,用户提供了我们稍后需要分析的一些动作)。从30到50每秒添加新项目。 我需要的是创建一个工作程序,它将查看此队列并对该数据执行某些任务。我可以这样做:

class Worker
{

    public function run()
    {
        $queue = new Queue('exchange', 'queue');
        while (true)
        {
            $queue->processQueue();
        }
    }
}

然后在服务器上运行worker.php,这似乎正在发挥作用。

但是我想知道如果没有数据可以继续,这个无限循环是否会给我的兔子实例增加额外负载?也许更好的方法是做一些事情:

class Worker
{
    CONST IDLE = 5;

    private $start = 0;

    public function run()
    {
        $this->start = time();

        $queue = new Queue('exchange', 'queue');
        while (true)
        {
            $queue->processQueue();

            //don't allow this worker to be working a lot
            if (time() - $this->start >= 60 * 60 - self::IDLE)
            {
                break;
            }

            sleep(self::IDLE);
        }

        $queue->close();
    }
}

所以我的工作不会持续从兔子那里提取数据但是会睡一会儿。经过一个小时的工作,它将停止工作,另一个工人实例将被crontab工作或其他东西调用?

1 个答案:

答案 0 :(得分:3)

为了使用rabbitmq管理我的工作人员,我使用以下库:

https://github.com/php-amqplib/php-amqplib

然后我创建了一个定义我的工作者应该如何工作的类(包含所有rabbitmq逻辑),它给了我这样的东西:

use PhpAmqpLib\Connection\AMQPStreamConnection;
use PhpAmqpLib\Message\AMQPMessage;

abstract class QueueAMQPConsumer
{    
    protected $connection;

    protected $debug;

    protected $queueName;

    protected $exchange;

    public function __construct(AMQPStreamConnection $AMQPConnection, $queueName, $exchange = null)
    {
        $this->connection = $AMQPConnection;
        $this->queueName = $queueName;
        $this->exchange = $exchange;
    }

    public function run($debug = false)
    {
        $this->debug = $debug;
        $channel = $this->connection->channel();
        if ($this->exchange !== null) {
            $channel->exchange_declare($this->exchange, "topic", false, true, false);
        }

        $channel->queue_declare($this->queueName, false, true, false, false);
        if ($this->exchange !== null) {
            $channel->queue_bind($this->queueName, $this->exchange);
        }

        $channel->basic_qos(null, 1, null);
        $channel->basic_consume($this->queueName, '', false, false, false, false, [$this, 'callback']);

        while (count($channel->callbacks)) {
            $channel->wait();
        }

        $channel->close();
        $this->connection->close();
    }


    final function callback(AMQPMessage $message)
    {
        $result = $this->process($message);

        if (false === $result) {
            $message->delivery_info['channel']->basic_nack($message->delivery_info['delivery_tag'], false, true);
        } else {
            $message->delivery_info['channel']->basic_ack($message->   delivery_info['delivery_tag']);
        }
    }

    /**
     * @param AMQPMessage $message
     *
     * @return bool
     */
    abstract protected function process(AMQPMessage $message);
}

此类允许设置队列,交换(在本例中为主题),QoS(您可以自定义所有这些参数,这只是一个示例)等。

然后它将循环回调。这里的回调是抽象方法进程(...),它将在需要处理队列的不同工作者上实现。因此,“循环/收听”的责任在于频道:$channel->wait();

然后我将创建一个需要处理队列中消息的子类:

class MyWorker extends QueueAMQPConsumer
{
    protected function process(AMQPMessage $message)
    {   
       // .... process your message here
    }
}

因此,工作人员将一直监听您的队列,并在他们到达队列时处理这些消息。 如果您的process(...)返回的内容不是false,则会确认该消息。

你必须像这样启动你的课程:

$consumer = new MyWorker(....);    
$consumer->run();