RabbitMQ 是基于高级消息队列协议(AMQP)的消息队列系统,擅长低延迟、高可用的消息传递。
如何使用
-
下载依赖
- Erlang 语言:www.erlang.org/downloads
- RabbitMQ:www.rabbitmq.com/docs/downlo...
- PHP 安装 amqp 插件:pecl.php.net/package/amq...
- ThinkPHP 安装插件:
composer update php-amqplib/php-amqplib
(版本为 3.0)
-
启动服务与网页管理
- 开启 RabbitMQ 服务:在 cmd 管理员模式下执行
net start RabbitMQ
- 开启 RabbitMQ 网页管理:在 cmd 管理员模式下执行
rabbitmq-plugins enable rabbitmq_management
- 访问:重新打开浏览器,访问 http://localhost:15672,用户名:
guest
,密码:guest
- 开启 RabbitMQ 服务:在 cmd 管理员模式下执行
-
使用说明
- 正式使用时,生产者和消费者都需要创建队列、创建交换机,并绑定路由。
- 若未指定交换机和绑定路由,直接创建队列,RabbitMQ 会走默认交换机。默认交换机会将消息路由到队列名称与路由键相同的队列,优点是快速开发,缺点是灵活性差,无法利用死信队列、延迟队列等高级特性。
如何保证 RabbitMQ 消息的可靠性?
-
生产者端 :开启发布确认(
confirmSelect()
),通过回调确认消息到达交换机。 -
Broker 端:
- 持久化交换机(
durable=true
) - 持久化队列(
durable=true
) - 持久化消息(
delivery_mode=2
)
- 持久化交换机(
-
消费者端:
- 手动 Ack,关闭自动确认(
auto_ack=false
) - 处理完成后调用
basic_ack
,避免消费中途失败导致消息丢失 - 利用死信队列
- 手动 Ack,关闭自动确认(
死信队列的用途及实现方式?
-
用途:
- 处理消费失败的信息
- 处理超时未消费的信息
- 实现消息重试机制
-
实现方式:
-
声明死信交换机和队列:
bash$channel->exchange_declare('dlx_exchange', 'direct', false, true, false); $channel->queue_declare('dlx_queue', false, true, false, false); $channel->queue_bind('dlx_queue', 'dlx_exchange', 'dlx_key');
-
源队列绑定死信配置:
dart$channel->queue_declare('source_queue', false, true, false, false, false, [ 'x-dead-letter-exchange' => 'dlx_exchange', // 死信交换机 'x-dead-letter-routing-key' => 'dlx_key', // 死信路由键 'x-message-ttl' => 60000 // 消息存活时间(毫秒) ]);
-
消费者监听死信队列:
php$channel->basic_consume('dlx_queue', '', false, true, false, false, $callback); // 自动确认,直接记录日志或人工处理
-
-
场景举例:订单支付超时未完成,将消息放入死信队列,触发自动取消订单或人工跟进。
如何实现延迟队列,有哪些方案?
-
TTL + 死信队列组合 :通过
x-message-ttl
或x-expires
设置消息 / 队列过期时间,过期后自动转入死信队列触发消费。 -
使用 rabbitmq_delayed_message_exchange 插件:
php$channel->exchange_declare('delayed_exchange', 'x-delayed-message', false, true, false, false, [ 'x-delayed-type' => 'direct' // 支持 direct/fanout/topic 类型 ]); $msg = new AMQPMessage('delayed message', ['x-delay' => 5000]); // 5秒延迟 $channel->basic_publish($msg, 'delayed_exchange', 'delay_key');
使用 Demo(PHP + ThinkPHP)
php
namespace app\controller;
use PhpAmqpLib\Connection\AMQPStreamConnection;
use PhpAmqpLib\Message\AMQPMessage;
use think\facade\Log;
class RabbitMQDemo
{
private $connection;
private $channel;
static $data = [];
public function __construct()
{
try {
// 创建连接 - 使用新版本的连接方式
$this->connection = new AMQPStreamConnection(
'localhost', // host
5672, // port
'guest', // user
'guest', // password
'/', // vhost
false, // insist
'AMQPLAIN', // login_method
null, // login_response
'en_US', // locale
3.0, // connection_timeout
3.0, // read_write_timeout
null, // context
false, // keepalive
0 // heartbeat
);
$this->channel = $this->connection->channel();
} catch (\Exception $e) {
Log::error('RabbitMQ连接失败:' . $e->getMessage());
throw $e;
}
}
/**
* 发送消息
*/
public function send()
{
try {
$exchange = 'my_exchange';
$queue = 'hello';
$routing_key = 'my_routing_key';
// 声明交换机
$this->channel->exchange_declare($exchange, 'direct', false, true, false);
// 声明队列
$this->channel->queue_declare($queue, false, true, false, false);
// 绑定队列跟交换机
$this->channel->queue_bind($queue, $exchange, $routing_key);
// 准备消息
$message = [
'id' => uniqid(),
'time' => date('Y-m-d H:i:s'),
'data' => 'Hello World!s'
];
// 创建消息对象
$msg = new AMQPMessage(
json_encode($message),
['delivery_mode' => AMQPMessage::DELIVERY_MODE_PERSISTENT]
);
// 发送消息
$this->channel->basic_publish($msg, $exchange, $routing_key);
// 关闭连接
$this->channel->close();
$this->connection->close();
return json(['code' => 200, 'msg' => '消息发送成功', 'data' => $message]);
} catch (\Exception $e) {
Log::error('发送消息失败:' . $e->getMessage());
return json(['code' => 500, 'msg' => '发送消息失败:' . $e->getMessage()]);
}
}
/**
* 接收消息
*/
public function receive()
{
try {
$exchange = 'my_exchange';
$queue = 'hello';
$routing_key = 'my_routing_key';
// 声明交换机
$this->channel->exchange_declare($exchange, 'direct', false, true, false);
// 声明队列
$this->channel->queue_declare($queue, false, true, false, false);
// 绑定队列跟交换机
$this->channel->queue_bind($queue, $exchange, $routing_key);
// 设置每次只处理一条消息
$this->channel->basic_qos(null, 1, null);
// 获取队列中的消息数量
$queueInfo = $this->channel->queue_declare($queue, false, true, false, false);
$messageCount = $queueInfo[1]; // 队列中的消息数量
if ($messageCount == 0) {
$this->channel->close();
$this->connection->close();
return json(['code' => 200, 'msg' => '队列中没有消息', 'count' => 0]);
}
$data = [];
// 定义回调函数
$callback = function($msg) {
$data = json_decode($msg->body, true);
self::$data = $data;
Log::info('收到消息:' . json_encode($data, JSON_UNESCAPED_UNICODE));
// 确认消息已处理
$msg->ack();
};
// 开始消费
$this->channel->basic_consume($queue, '', false, false, false, false, $callback);
// 只等待一条消息,设置超时时间
try {
$this->channel->wait(null, true, 2); // 等待2秒
} catch (\PhpAmqpLib\Exception\AMQPTimeoutException $e) {
// 超时处理
Log::info('等待消息超时');
}
// 关闭连接
$this->channel->close();
$this->connection->close();
return json([
'code' => 200,
'msg' => '消息接收完成',
'count' => $messageCount,
'data' => self::$data,
'received' => true
]);
} catch (\Exception $e) {
Log::error('接收消息失败:' . $e->getMessage());
return json(['code' => 500, 'msg' => '接收消息失败:' . $e->getMessage()]);
}
}
}