备份与恢复

定期备份 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 导出每天
消息备份持久化消息数据目录按需
配置备份配置文件文件复制变更时

备份检查清单

  • [ ] 定期导出定义文件
  • [ ] 验证备份文件完整性
  • [ ] 定期进行恢复演练
  • [ ] 备份文件异地存储
  • [ ] 监控备份任务状态
  • [ ] 文档化恢复流程

下一步

备份策略就绪后,让我们学习 故障排查