[TOC] #### 1. 前言 --- 上一讲我们解决了消费者挂掉的问题:用 ACK 机制,消息不会丢,但还有个问题:RabbitMQ 服务本身重启了怎么办 ? 服务器断电了、Docker 容器重启了、RabbitMQ 进程崩了,队列里的消息还在吗 ? + 答案是:默认不在。是的,又是默认行为。 默认情况下,RabbitMQ 把消息存在内存里,服务重启,内存清空,消息就没了 心想:这也太不靠谱了吧 ?其实这是性能考虑,内存操作比磁盘快得多,但对于重要的业务场景,必须手动开启持久化 #### 2. 怎么让消息不丢 --- 要保证 RabbitMQ 重启后消息还在,需要同时做两件事:队列持久化、消息持久化 第一件事:队列持久化 + 这样队列的元数据会写到磁盘上,RabbitMQ 重启后,队列还在 + 但注意:队列持久化只保证队列这个「壳子」还在,不保证里面的消息还在 ```php // 队列持久化:声明队列的时候第三个参数设置为 true $channel->queue_declare('order_queue', false, true, false, false); ``` 第二件事:消息持久化 创建消息的时候,设置消息的投递模式为持久化: ```php $message = new AMQPMessage(json_encode($data), [ // DELIVERY_MODE_PERSISTENT 表示消息需要持久化,RabbitMQ 会把这条消息写到磁盘上 'delivery_mode' => AMQPMessage::DELIVERY_MODE_PERSISTENT, ]); ``` 两件事缺一不可: + 只做队列持久化:队列在,消息没了 + 只做消息持久化:队列没了,消息也没地方存 + 队列和消息都做持久化:队列在,消息也在。两个都要做,才能真正保证消息不丢 #### 3. 代码完整示例 --- 发送持久化消息代码示例: ```php require_once __DIR__ . '/vendor/autoload.php'; use PhpAmqpLib\Connection\AMQPStreamConnection; use PhpAmqpLib\Message\AMQPMessage; $connection = new AMQPStreamConnection('192.168.3.20', 5672, 'admin', 'admin123'); $channel = $connection->channel(); // 1. 声明持久化队列 $channel->queue_declare('order_queue', false, true, false, false); // 2. 创建持久化消息 $data = ['user_id' => 1, 'order_id' => mt_rand(1000, 9999)]; $message = new AMQPMessage(json_encode($data), [ 'delivery_mode' => AMQPMessage::DELIVERY_MODE_PERSISTENT, ]); // 3. 发送 $channel->basic_publish($message, '', 'order_queue'); echo "持久化消息已发送\n"; // 4. 关闭连接 $channel->close(); $connection->close(); ``` 现在你可以测试一下: + 发一条消息,也可以多发几条 + 重启 RabbitMQ: `docker restart rabbitmq` + 打开管理界面,看消息还在不在,如果配置正确,消息就还在 #### 4. 有了持久化就万无一失了吗 --- 并不是,持久化能应对 RabbitMQ 重启的情况,但不能应对所有情况 比如: + 磁盘损坏了 + 消息刚到内存,还没来得及写磁盘,RabbitMQ 就崩了(极小概率) 对于绝大多数业务场景,队列持久化 + 消息持久化 + 手动 ACK 已经够用了 如果你的业务对可靠性要求极高(比如金融交易),还可以用发布确认(Publisher Confirm)来进一步保证,这个后面会讲 #### 5. 本文小结 --- 默认情况下,RabbitMQ 重启后消息会丢,要保证不丢,需要同时做两件事: + 队列持久化:声明队列时 `durable = true` + 消息持久化:发送消息时 `delivery_mode = DELIVERY_MODE_PERSISTENT` 再加上上一讲的 `手动 ACK`,就构成了 RabbitMQ 消息可靠性的基础三件套: + 队列持久化 + 消息持久化 + 手动 ACK 记住这个组合,实际开发中一定要用 下一讲,我们来聊聊消息本身,除了内容,它还能携带什么信息 ?