php RabbitMQ 类

<?php

namespace Common;

use PhpAmqpLib\Channel\AMQPChannel;
use PhpAmqpLib\Connection\AMQPStreamConnection;
use PhpAmqpLib\Message\AMQPMessage;
use PhpAmqpLib\Wire\AMQPTable;

class RabbitMQ
{
    /**
     * 交换机类型
     */
    const EXCHANGE_TYPE_DIRECT = 'direct';
    const EXCHANGE_TYPE_FANOUT = 'fanout';
    const EXCHANGE_TYPE_TOPIC = 'topic';
    const EXCHANGE_TYPE_HEADERS = 'headers';

    /**
     * @var RabbitMQ 实例
     */
    private static $instance;

    /**
     * @var AMQPStreamConnection 连接
     */
    public $connection;

    /**
     * @var AMQPChannel 通道
     */
    private $channel;

    /**
     * 获取实例
     * @param string|null $vhost 虚拟主机
     * @return RabbitMQ 实例
     */
    public static function getInstance(string $vhost = null)
    {
        if (!(self::$instance instanceof self)) {
            self::$instance = new self($vhost);
        }
        return self::$instance;
    }

    /**
     * 构造
     * @param string|null $vhost 虚拟主机
     */
    private function __construct(string $vhost = null)
    {
        // 创建连接
        $config = DI()->config->get('sys.rabbitmq');
        $config['vhost'] = $vhost ?: $config['vhost'];

        $this->connection = new AMQPStreamConnection($config['host'], $config['port'], $config['user'], $config['password'], $config['vhost'], heartbeat: 20);
        // 创建通道
        $this->channel = $this->connection->channel();
    }

    /**
     * 克隆
     * @return void
     */
    private function __clone()
    {
    }

    /**
     * 析构
     * @throws \Exception
     */
    public function __destruct()
    {
        // 关闭通道
        $this->channel->close();
        // 关闭连接
        $this->connection->close();
    }

    /**
     * 开启生产确认
     * @param callable|null $ackHandler 生产确认成功处理函数
     * @param callable|null $uackHandler 生产确认失败处理函数
     * @return void
     */
    public function enablePublisherConfirms(callable $ackHandler = null, callable $uackHandler = null)
    {
        // 设置生产确认成功处理函数
        $callback = function (AMQPMessage $msg) use ($ackHandler) {
            if (is_callable($ackHandler)) {
                $ackHandler($msg);
            }
        };
        $this->channel->set_ack_handler($callback);

        // 设置生产确认失败处理函数
        $callback = function (AMQPMessage $msg) use ($uackHandler) {
            if (is_callable($uackHandler)) {
                $uackHandler($msg);
            }
        };
        $this->channel->set_nack_handler($callback);

        // 开启生产确认
        $this->channel->confirm_select();
    }

    /**
     * 等待生产确认
     * @return void
     */
    public function waitForPublisherConfirms()
    {
        $this->channel->wait_for_pending_acks();
    }

    /**
     * 生产消息
     * @param string $exchange 交换机名
     * @param string $routingKey 路由键
     * @param string $msgBody 消息内容
     * @param array $msgProperties 消息属性
     * @return void
     */
    private function produce(string $exchange, string $routingKey, string $msgBody, array $msgProperties = [])
    {
        // 创建消息
        $msgProperties['delivery_mode'] = AMQPMessage::DELIVERY_MODE_PERSISTENT;
        $msg = new AMQPMessage($msgBody, $msgProperties);

        // 生产消息
        $this->channel->basic_publish($msg, $exchange, $routingKey);
    }

    /**
     * 工作队列模式-生产消息
     * @param string $queue 队列名
     * @param string $msgBody 消息内容
     * @param array $msgProperties 消息属性
     * @return void
     */
    public function produceWorkQueues(string $queue, string $msgBody, array $msgProperties = [], AMQPTable $arguments = null)
    {
        // 声明队列,并设置死信交换机和路由键
        $this->channel->queue_declare($queue, false, true, false, false, false, $arguments);
        // 生产消息
        $this->produce('', $queue, $msgBody, $msgProperties);
    }

    /**
     * 声明死信队列
     * @param string $queue 队列名
     * @param string $dlxExchangeName 死信交换机名
     * @param string $dlxRoutingKey 死信交换机名
     * @return void
     */
    public function declareDeadLetterQueue(string $queue, string $dlxExchangeName, string $dlxRoutingKey)
    {
        $queueName = "{$queue}_dlx";
        // 声明公用的死信交换机
        $this->channel->exchange_declare($dlxExchangeName, 'direct', false, true, false);

        // 声明死信队列
        $this->channel->queue_declare($queueName, false, true, false, false);

        // 将死信队列绑定到死信交换机
        $this->channel->queue_bind($queueName, $dlxExchangeName, $dlxRoutingKey);
    }
}

 

posted @ 2025-02-27 11:20  jk波  阅读(3)  评论(0)    收藏  举报