Laravel RabbitMQ 工作佇列

Loki_Q發表於2020-05-12

Laravel工作佇列

安裝元件

~ composer require php-amqplib/php-amqplib

建立一個初始化抽象類 InitAbstract

對連線及通用的定義進行簡單的封裝

<?php
/**
 * User: Loki.Q
 * Date: 2020/5/12
 * Time: 11:43
 */

namespace App\Services\RabbitMQ;


use PhpAmqpLib\Connection\AMQPStreamConnection;

/**
 * Class InitAbstract
 * @package App\Services\RabbitMQ
 */
abstract class InitAbstract
{
    /**
     * @var AMQPStreamConnection
     */
    protected $connection;

    /**
     * HOST地址
     * @var string
     */
    private $host = '127.0.0.1';

    /**
     * 埠
     * @var string
     */
    private $port = '5672';

    /**
     * 賬號
     * @var string
     */
    private $user = 'test001';

    /**
     * 密碼
     * @var string
     */
    private $passwd = 'test001';

    /**
     * vhost
     * @var string
     */
    private $vhost = 'hello';

    /**
     * 佇列名稱
     * @var string
     */
    protected $queueName = 'test_queue';

    /**
     * 連線RabbitMQ
     * InitAbstract constructor.
     */
    public function __construct()
    {
        $this->connection = new AMQPStreamConnection(
            $this->host,
            $this->port,
            $this->user,
            $this->passwd,
            $this->vhost
        );
    }

    /**
     * 定義頻道
     * @return \PhpAmqpLib\Channel\AMQPChannel
     */
    protected function channel()
    {
        $channel = $this->connection->channel();

        /**
         * 為了不讓佇列消失,需要把佇列宣告為持久化(durable)。
         * 為此我們通過queue_declare的第三引數為 true
         */
        $channel->queue_declare(
            $this->queueName, false, true, false, false
        );
        return  $channel;
    }
}

定義一個生產者類 Producer

<?php
/**
 * User: Loki.Q
 * Date: 2020/5/12
 * Time: 11:51
 */

namespace App\Services\RabbitMQ;


use PhpAmqpLib\Message\AMQPMessage;

/**
 * Class Producer
 * @package App\Services\RabbitMQ
 */
class Producer extends InitAbstract
{
    /**
     * 釋出訊息
     */
    public function push()
    {
        $channel = $this->channel();

        $data = 'this is message';

        $msg = new AMQPMessage($data, [
            //訊息持久化
            'delivery_mode' => AMQPMessage::DELIVERY_MODE_PERSISTENT
        ]);
        $channel->basic_publish(
            $msg, '', $this->queueName
        );

        echo " [x] Sent ", $data, "\n";

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

定義消費者類 Consumer

<?php
/**
 * User: Loki.Q
 * Date: 2020/5/12
 * Time: 13:44
 */

namespace App\Services\RabbitMQ;

/**
 * Class Consumer
 * @package App\Services\RabbitMQ
 */
class Consumer extends InitAbstract
{
    /**
     * @throws \ErrorException
     */
    public function wait()
    {
        $channel = $this->channel();

        echo " [*] Waiting for messages. To exit press CTRL+C\n";

        $callback = function ($msg) {
            echo " [x] Received ", $msg->body, "\n";
            sleep(substr_count($msg->body, '.'));
            echo " [x] Done", "\n";

            //手動確認訊息
            $msg->delivery_info['channel']->basic_ack($msg->delivery_info['delivery_tag']);
        };

        /**
         * 這時因為RabbitMQ只管分發進入佇列的訊息,不會關心有多少消費者(consumer)沒有作出響應。
         * 它盲目的把第n-th條訊息發給第n-th個消費者。
         * 我們可以使用basic.qos方法,並設定prefetch_count=1。
         * 訊息並且作出了響應。
         * 這樣,RabbitMQ就會把訊息分發給下一個空閒的工作者(worker)。
         */
        $channel->basic_qos(null, 1, null);
        $channel->basic_consume(
            $this->queueName, '', false, false, false, false, $callback
        );

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

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

routes/console.php中定義命令

監聽消費

Artisan::command('queue', function (\App\Services\RabbitMQ\Consumer $consumer) {
    $consumer->wait();
})->describe('rabbitmq queue');

定義一個Controller來執行生產

<?php
/**
 * User: Loki.Q
 * Date: 2020/5/12
 * Time: 13:37
 */

namespace App\Http\Controllers;


use App\Services\RabbitMQ\Producer;

class ProducerController extends Controller
{
    public function handle(Producer $producer)
    {
        return $producer->push();
    }
}

定義好路由後,執行生產

~ curl http://xx.test/producer/push
 [x] Sent this is message

結果

~ php artisan queue
 [*] Waiting for messages. To exit press CTRL+C
 [x] Received this is message
 [x] Done
 [x] Received this is message
 [x] Done
 [x] Received this is message
 [x] Done
本作品採用《CC 協議》,轉載必須註明作者和本文連結

相關文章