备份与恢复
定期备份 RabbitMQ 数据对于保障业务连续性至关重要。
🎯 本章目标
- 了解 RabbitMQ 需要备份的内容
- 掌握多种备份方法
- 学会快速恢复数据
📦 备份内容
需要备份的数据
| 数据类型 | 位置 | 说明 |
|---|---|---|
| 定义数据 | 管理 API 导出 | 交换机、队列、绑定、用户、权限 |
| 消息数据 | 数据目录 | 持久化消息 |
| 配置文件 | /etc/rabbitmq/ | rabbitmq.conf, enabled_plugins |
数据目录位置
| 系统 | 默认路径 |
|---|---|
| Linux (包管理) | /var/lib/rabbitmq/mnesia/ |
| Docker | /var/lib/rabbitmq/mnesia/ |
| Windows | %APPDATA%\RabbitMQ\db\ |
📤 定义导出(推荐)
导出所有定义
# 使用 rabbitmqadmin
rabbitmqadmin export definitions.json
# 使用 API
curl -u admin:password -X GET \
"http://localhost:15672/api/definitions" \
> definitions.json
导出特定 vhost
curl -u admin:password -X GET \
"http://localhost:15672/api/definitions/%2f" \
> vhost-definitions.json
定义文件内容
{
"rabbit_version": "3.12.0",
"rabbitmq_version": "3.12.0",
"users": [
{
"name": "admin",
"password_hash": "...",
"tags": ["administrator"]
}
],
"vhosts": [
{"name": "/"}
],
"permissions": [...],
"queues": [
{
"name": "order.queue",
"vhost": "/",
"durable": true,
"auto_delete": false,
"arguments": {}
}
],
"exchanges": [...],
"bindings": [...],
"policies": [...]
}
导入定义
# 使用 rabbitmqadmin
rabbitmqadmin import definitions.json
# 使用 API
curl -u admin:password -X POST \
-H "Content-Type: application/json" \
-d @definitions.json \
"http://localhost:15672/api/definitions"
💾 消息数据备份
方法一:停机备份
#!/bin/bash
# backup-full.sh
BACKUP_DIR="/backup/rabbitmq/$(date +%Y%m%d_%H%M%S)"
MNESIA_DIR="/var/lib/rabbitmq/mnesia"
# 1. 停止 RabbitMQ
systemctl stop rabbitmq-server
# 2. 备份数据目录
mkdir -p $BACKUP_DIR
cp -r $MNESIA_DIR $BACKUP_DIR/
# 3. 备份配置文件
cp -r /etc/rabbitmq $BACKUP_DIR/
# 4. 导出定义(需要先启动)
systemctl start rabbitmq-server
sleep 10
rabbitmqadmin export $BACKUP_DIR/definitions.json
echo "备份完成: $BACKUP_DIR"
方法二:热备份(仅定义)
#!/bin/bash
# backup-definitions.sh
BACKUP_DIR="/backup/rabbitmq/definitions"
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
mkdir -p $BACKUP_DIR
# 导出定义
rabbitmqadmin export $BACKUP_DIR/definitions_$TIMESTAMP.json
# 保留最近 30 天的备份
find $BACKUP_DIR -name "definitions_*.json" -mtime +30 -delete
echo "定义备份完成: $BACKUP_DIR/definitions_$TIMESTAMP.json"
方法三:消息队列备份
package com.example.rabbitmq.backup;
import com.rabbitmq.client.*;
import lombok.extern.slf4j.Slf4j;
import java.io.*;
import java.nio.charset.StandardCharsets;
/**
* 消息备份工具
* 通过消费消息并保存到文件进行备份
*/
@Slf4j
public class MessageBackup {
public static void backupQueue(String queueName, String backupFile) throws Exception {
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("localhost");
factory.setUsername("admin");
factory.setPassword("password");
try (Connection connection = factory.newConnection();
Channel channel = connection.createChannel();
BufferedWriter writer = new BufferedWriter(new FileWriter(backupFile))) {
int count = 0;
while (true) {
// 不自动确认,备份后确认
GetResponse response = channel.basicGet(queueName, false);
if (response == null) {
break; // 队列为空
}
String message = new String(response.getBody(), StandardCharsets.UTF_8);
// 写入备份文件(JSON Lines 格式)
writer.write(message);
writer.newLine();
// 重新入队(备份不消费)
channel.basicNack(response.getEnvelope().getDeliveryTag(), false, true);
count++;
}
log.info("备份完成,共 {} 条消息", count);
}
}
public static void main(String[] args) throws Exception {
backupQueue("order.queue", "/backup/order_messages.jsonl");
}
}
🔄 恢复流程
恢复定义
#!/bin/bash
# restore-definitions.sh
BACKUP_FILE=$1
if [ -z "$BACKUP_FILE" ]; then
echo "用法: $0 <backup_file>"
exit 1
fi
# 1. 确保 RabbitMQ 运行
systemctl start rabbitmq-server
sleep 10
# 2. 导入定义
rabbitmqadmin import $BACKUP_FILE
echo "定义恢复完成"
恢复数据目录
#!/bin/bash
# restore-full.sh
BACKUP_DIR=$1
MNESIA_DIR="/var/lib/rabbitmq/mnesia"
if [ -z "$BACKUP_DIR" ]; then
echo "用法: $0 <backup_directory>"
exit 1
fi
# 1. 停止 RabbitMQ
systemctl stop rabbitmq-server
# 2. 备份当前数据(以防万一)
mv $MNESIA_DIR ${MNESIA_DIR}.old.$(date +%s)
# 3. 恢复备份数据
cp -r $BACKUP_DIR/mnesia $MNESIA_DIR
chown -R rabbitmq:rabbitmq $MNESIA_DIR
# 4. 恢复配置文件
cp -r $BACKUP_DIR/rabbitmq/* /etc/rabbitmq/
# 5. 启动 RabbitMQ
systemctl start rabbitmq-server
# 6. 验证
sleep 10
rabbitmqctl status
echo "恢复完成"
恢复消息
package com.example.rabbitmq.backup;
import com.rabbitmq.client.*;
import lombok.extern.slf4j.Slf4j;
import java.io.*;
import java.nio.charset.StandardCharsets;
@Slf4j
public class MessageRestore {
public static void restoreQueue(String queueName, String backupFile) throws Exception {
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("localhost");
factory.setUsername("admin");
factory.setPassword("password");
try (Connection connection = factory.newConnection();
Channel channel = connection.createChannel();
BufferedReader reader = new BufferedReader(new FileReader(backupFile))) {
String line;
int count = 0;
while ((line = reader.readLine()) != null) {
channel.basicPublish("", queueName,
MessageProperties.PERSISTENT_TEXT_PLAIN,
line.getBytes(StandardCharsets.UTF_8));
count++;
}
log.info("恢复完成,共 {} 条消息", count);
}
}
public static void main(String[] args) throws Exception {
restoreQueue("order.queue", "/backup/order_messages.jsonl");
}
}
🐳 Docker 备份
Docker 备份脚本
#!/bin/bash
# docker-backup.sh
CONTAINER_NAME="rabbitmq"
BACKUP_DIR="/backup/rabbitmq/$(date +%Y%m%d_%H%M%S)"
mkdir -p $BACKUP_DIR
# 1. 导出定义
docker exec $CONTAINER_NAME rabbitmqadmin export /tmp/definitions.json
docker cp $CONTAINER_NAME:/tmp/definitions.json $BACKUP_DIR/
# 2. 备份数据卷(需要停止容器)
docker stop $CONTAINER_NAME
docker run --rm \
-v rabbitmq_data:/source:ro \
-v $BACKUP_DIR:/backup \
alpine tar czf /backup/data.tar.gz -C /source .
docker start $CONTAINER_NAME
echo "Docker 备份完成: $BACKUP_DIR"
Docker 恢复脚本
#!/bin/bash
# docker-restore.sh
CONTAINER_NAME="rabbitmq"
BACKUP_DIR=$1
if [ -z "$BACKUP_DIR" ]; then
echo "用法: $0 <backup_directory>"
exit 1
fi
# 1. 停止容器
docker stop $CONTAINER_NAME
# 2. 恢复数据卷
docker run --rm \
-v rabbitmq_data:/target \
-v $BACKUP_DIR:/backup:ro \
alpine sh -c "rm -rf /target/* && tar xzf /backup/data.tar.gz -C /target"
# 3. 启动容器
docker start $CONTAINER_NAME
sleep 10
# 4. 导入定义
docker cp $BACKUP_DIR/definitions.json $CONTAINER_NAME:/tmp/
docker exec $CONTAINER_NAME rabbitmqadmin import /tmp/definitions.json
echo "Docker 恢复完成"
⏰ 自动备份
Cron 定时任务
# 编辑 crontab
crontab -e
# 每天凌晨 2 点备份定义
0 2 * * * /opt/scripts/backup-definitions.sh >> /var/log/rabbitmq-backup.log 2>&1
# 每周日凌晨 3 点完整备份
0 3 * * 0 /opt/scripts/backup-full.sh >> /var/log/rabbitmq-backup.log 2>&1
备份验证脚本
#!/bin/bash
# verify-backup.sh
BACKUP_FILE=$1
# 验证 JSON 格式
if ! python3 -m json.tool $BACKUP_FILE > /dev/null 2>&1; then
echo "❌ 备份文件格式错误: $BACKUP_FILE"
exit 1
fi
# 检查必要字段
QUEUES=$(jq '.queues | length' $BACKUP_FILE)
EXCHANGES=$(jq '.exchanges | length' $BACKUP_FILE)
USERS=$(jq '.users | length' $BACKUP_FILE)
echo "✅ 备份验证通过"
echo " 队列数: $QUEUES"
echo " 交换机数: $EXCHANGES"
echo " 用户数: $USERS"
📝 本章小结
| 备份类型 | 内容 | 方法 | 频率建议 |
|---|---|---|---|
| 定义备份 | 配置元数据 | API 导出 | 每天 |
| 消息备份 | 持久化消息 | 数据目录 | 按需 |
| 配置备份 | 配置文件 | 文件复制 | 变更时 |
备份检查清单
- [ ] 定期导出定义文件
- [ ] 验证备份文件完整性
- [ ] 定期进行恢复演练
- [ ] 备份文件异地存储
- [ ] 监控备份任务状态
- [ ] 文档化恢复流程
下一步
备份策略就绪后,让我们学习 故障排查!
