<?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);
}
}