php使用RabbitMQ实现队列操作

使用之前需要安装服务:centos环境安装rabbitmq服务

配置项:

<?php
namespace app\common\library\rabbitMQ;

const HOST = '127.0.0.1';
const PORT = 5672;

const USER = 'guest';
const PASS = 'guest';

const EXCHANGE_NAME= 'router';
const QUEUE_NAME = 'msgs';
const ROUTING_KEY = 'routing_key';
const DELAY_QUEUE_NAME = 'delayed_queue';
const DELAY_EXCHANGE_NAME = 'delayed_exchange';
const DELAY_ROUTING_KEY = 'delayed_key';

定义基类服务:

<?php
namespace app\common\library\rabbitMQ;

use PhpAmqpLib\Connection\AMQPStreamConnection;
use PhpAmqpLib\Exchange\AMQPExchangeType;
use PhpAmqpLib\Wire\AMQPTable;

include_once(__DIR__ .'/mq_config.php');
class BaseMQService
{
    /**
     * MQ events must be declared before use
     */
    const PLATFORM_EVENT = [
       /** 平台支持的事件列表,EventPool内部定义,可自由拓展 **/
    ];

    protected static $_instance;
    protected $connection;
    protected $channel;
    protected $delay = false;
    public function __construct($delay = 0)
    {
        $this->connection =  new AMQPStreamConnection(HOST, PORT, USER, PASS);
        $this->channel = $this->connection->channel();
        $this->delay = $delay;

        if($this->delay){
            $args = new AMQPTable(['x-delayed-type' => AMQPExchangeType::FANOUT]);
            $this->channel->exchange_declare(DELAY_EXCHANGE_NAME, 'x-delayed-message', false, true, false,false,false,$args);
            $this->channel->queue_declare(DELAY_QUEUE_NAME, false, true, false, false, false,
                new AMQPTable(['x-dead-letter-exchange' => 'delayed'])
            );
            $this->channel->queue_bind(DELAY_QUEUE_NAME, DELAY_EXCHANGE_NAME, DELAY_ROUTING_KEY);
        }else{
            $this->channel->exchange_declare(EXCHANGE_NAME, AMQPExchangeType::DIRECT, false, true, false);
            $this->channel->queue_declare(QUEUE_NAME, false, true, false, false);
            $this->channel->queue_bind(QUEUE_NAME, EXCHANGE_NAME, ROUTING_KEY);
        }
    }

    public static function getInstance($delay = 0): BaseMQService
    {
        if (is_null(self::$_instance) || self::$_instance->delay != $delay){
            self::$_instance = new static($delay);
        }
        return self::$_instance;
    }

    public function __destruct()
    {
        $this->shutdown();
    }

    
    public function shutdown() : void
    {
        $this->channel->close();
        $this->connection->close();
        if (request()->isCli()){
            $this->consoleLog('closed');
        }

    }
    
    //控制台打印日志,调试使用,write_queue_logs方法可去除可使用日志记录插入日志数据
    protected function consoleLog($message = 'Waiting for message. To exit press CTRL+C ',$writeLog = false)
    {
        $message = date('Y-m-d H:i:s'). " [*]: " . $message;
        echo $message.PHP_EOL;
        if($writeLog) write_queue_logs($message);
    }


}

事件池,统一调用业务代码,定义业务事件类型:

<?php
namespace app\common\library\rabbitMQ;

use app\api\model\AiJob;
use app\api\model\BatchProducingTask;
use app\api\model\ProducingTask;
use app\api\model\Resource;
use app\api\model\ShareTask;
use app\api\service\AccountService;
use app\api\service\AliCloudService;
use app\api\service\VideoService;
use app\common\library\rabbitMQ\publish\MessageQueue;

class EventPool
{
    const EVENT_ERROR = 'error';

    public static function dispatch($event,$messageData)
    {
        extract($messageData);
        $token = $token ?? '';
        /***
            这里编写你的业务代码
        ***/
    }

    public static function handelErrorJob($event,$data)
    {
        $queue = new MessageQueue();
        $queue->sendMessage($event,$data);
    }
}

发布事件:

<?php
namespace app\common\library\rabbitMQ\publish;
use app\common\library\rabbitMQ\BaseMQService;
use app\common\library\rabbitMQ\MessageInterface;
use PhpAmqpLib\Message\AMQPMessage;
use PhpAmqpLib\Wire\AMQPTable;
use think\Env;
use const app\common\library\rabbitMQ\DELAY_EXCHANGE_NAME;
use const app\common\library\rabbitMQ\DELAY_ROUTING_KEY;
use const app\common\library\rabbitMQ\EXCHANGE_NAME;
use const app\common\library\rabbitMQ\ROUTING_KEY;

class MessageQueue extends BaseMQService implements MessageInterface
{
    /**
     * @param string $event
     * @param array $data
     * @return bool|void
     */
    public function sendMessage($event,$data = [],$delay = false)
    {
        if (!in_array($event,self::PLATFORM_EVENT) && !Env::get('app.debug')){
            errException('event was not allowed for this server');
        }

        $data['counter'] = $data['counter'] ?? 1;
        $messageBody = json_encode(compact('event','data'));
        $message = new AMQPMessage($messageBody, array('delivery_mode' => AMQPMessage::DELIVERY_MODE_PERSISTENT));

        if ($this->delay){
            $headers = new AMQPTable(['x-delay' => $this->delay * 1000]);
            $message->set('application_headers',$headers);
            $this->channel->basic_publish($message, DELAY_EXCHANGE_NAME,  DELAY_ROUTING_KEY);
        }else{
            $this->channel->basic_publish($message, EXCHANGE_NAME,  ROUTING_KEY);
        }
        return true;
    }

}

消费者代码:

基类消费者:

<?php
namespace app\common\library\rabbitMQ\consumer;

use app\api\model\MqErrorLogs;
use app\common\library\rabbitMQ\BaseMQService;
use app\common\library\rabbitMQ\EventPool;
use PhpAmqpLib\Message\AMQPMessage;

class BaseConsumer extends BaseMQService
{
    const CONSUMER_TAG = 'consumer';
    const DELAYED_CONSUMER_TAG = 'delayed_consumer';

    function processMessage(AMQPMessage $message)
    {
        $messageArr = json_decode($message->body,true);
//        var_export($messageArr['data']);
//        var_export(PHP_EOL);
        $event = $messageArr['event'];
        if (in_array($event,self::PLATFORM_EVENT)){
            $data = $messageData = $messageArr['data'] ?? [];
            if ($messageData['counter'] >= 5){
                $data = json_encode($data);
                MqErrorLogs::create(compact('event','data'));
                $this->consoleLog("job was completed with error:{$message->body}",true);
                $message->delivery_info['channel']->basic_ack($message->delivery_info['delivery_tag']);
            }else{
                $res = EventPool::dispatch($event,$messageData);
                if ($res === EventPool::EVENT_ERROR){
                    $this->consoleLog("job error:{$message->body}",true);
                    $message->delivery_info['channel']->basic_reject($message->delivery_info['delivery_tag'],false);
                    $messageData['counter']++;
                    EventPool::handelErrorJob($event,$messageData);
                }else{
                    if ($res){
                        $this->consoleLog("job was completed:{$message->body}",true);
                        $message->delivery_info['channel']->basic_ack($message->delivery_info['delivery_tag']);
                    }else{
                        $this->consoleLog("requeue the job:{$message->body}",true);
                        $message->nack(true);
                    }
                }
            }
        }else{
            $this->consoleLog("event not found:{$message->body}",true);
            $message->delivery_info['channel']->basic_reject($message->delivery_info['delivery_tag'],false);
        }

        // Send a message with the string "quit" to cancel the consumer.
        if ($message->body === 'quit') {
            $message->delivery_info['channel']->basic_cancel($message->delivery_info['consumer_tag']);
        }
    }
}

延时消费者:

<?php
namespace app\common\library\rabbitMQ\consumer;

use const app\common\library\rabbitMQ\DELAY_QUEUE_NAME;

class DelayedConsumer extends BaseConsumer
{
    public function start()
    {
        $this->channel->basic_qos('',1,'');
        $this->channel->basic_consume(DELAY_QUEUE_NAME, self::DELAYED_CONSUMER_TAG, false, false, false, false, array($this, 'processMessage'));

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

        register_shutdown_function(array($this, 'shutdown'));
        while (count($this->channel->callbacks)) {
            $this->channel->wait();
        }
        $this->consoleLog();
    }


}

 实时消费者:

<?php
namespace app\common\library\rabbitMQ\consumer;

use const app\common\library\rabbitMQ\DELAY_QUEUE_NAME;
use const app\common\library\rabbitMQ\QUEUE_NAME;

class MessageConsumer extends BaseConsumer
{
    public function start()
    {
        $processes = 5;
        for ($i = 0; $i < $processes;$i++){
            $this->channel->basic_qos('',1,'');
            $this->channel->basic_consume(QUEUE_NAME, self::CONSUMER_TAG . $i, false, false, false, false, array($this, 'processMessage'));
        }
        echo " [*] Waiting for messages. To exit press CTRL+C\n";

        register_shutdown_function(array($this, 'shutdown'));
        while (count($this->channel->callbacks)) {
            $this->channel->wait();
        }
        $this->consoleLog();
    }


}

启动消费者:

按需分开执行,以下为使用demo

<?php
namespace app\api\controller;

use app\common\library\rabbitMQ\consumer\DelayedConsumer;
use app\common\library\rabbitMQ\consumer\MessageConsumer;

class Queue
{
    public function index()
    {
        $consumer = new MessageConsumer();
        $consumer->start();
        echo "normal queue\n";

        $consumer = new DelayedConsumer();
        $consumer->start();
        echo "delayed queue\n";
    }
}

 投递任务:

<?php
//投递延时任务
$q = MessageQueue::getInstance(10);
$q->sendMessage(EventPool::TEST_JOB,$data['data']);

//投递实时任务
$q = MessageQueue::getInstance();
$q->sendMessage(EventPool::TEST_JOB,$data['data']);

  • 6
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是使用PHPRabbitMQ实现消息队列的步骤: 1.安装RabbitMQ 首先需要安装RabbitMQ,可以从官方网站下载并安装。 2.安装php-amqplib php-amqplib是一个PHP库,用于与RabbitMQ通信。可以使用Composer安装它。 3.创建发送消息的脚本 ```php <?php require_once __DIR__ . '/vendor/autoload.php'; use PhpAmqpLib\Connection\AMQPStreamConnection; use PhpAmqpLib\Message\AMQPMessage; $connection = new AMQPStreamConnection('localhost', 5672, 'guest', 'guest'); $channel = $connection->channel(); $channel->queue_declare('hello', false, false, false, false); $msg = new AMQPMessage('Hello World!'); $channel->basic_publish($msg, '', 'hello'); echo " [x] Sent 'Hello World!'\n"; $channel->close(); $connection->close(); ?> ``` 4.创建接收消息的脚本 ```php <?php require_once __DIR__ . '/vendor/autoload.php'; use PhpAmqpLib\Connection\AMQPStreamConnection; $connection = new AMQPStreamConnection('localhost', 5672, 'guest', 'guest'); $channel = $connection->channel(); $channel->queue_declare('hello', false, false, false, false); echo ' [*] Waiting for messages. To exit press CTRL+C', "\n"; $callback = function ($msg) { echo " [x] Received ", $msg->body, "\n"; }; $channel->basic_consume('hello', '', false, true, false, false, $callback); while (count($channel->callbacks)) { $channel->wait(); } $channel->close(); $connection->close(); ?> ``` 5.运行发送和接收脚本 在终端中运行发送脚本和接收脚本,可以看到消息被成功发送和接收。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值