故障排查

本章介绍 RabbitMQ 常见问题的诊断和解决方法。

🎯 本章目标

  • 掌握常见问题的诊断方法
  • 学会使用诊断工具
  • 了解问题解决的最佳实践

🔍 诊断命令

健康检查

# 快速健康检查
rabbitmq-diagnostics ping

# 完整健康检查
rabbitmq-diagnostics check_running
rabbitmq-diagnostics check_local_alarms
rabbitmq-diagnostics check_port_connectivity

# 所有检查
rabbitmq-diagnostics check_virtual_hosts

状态检查

# 节点状态
rabbitmqctl status

# 集群状态
rabbitmqctl cluster_status

# 环境信息
rabbitmqctl environment

资源检查

# 内存使用
rabbitmqctl status | grep -A 20 memory

# 文件描述符
rabbitmqctl status | grep file_descriptors

# 磁盘使用
rabbitmqctl status | grep disk

❌ 常见问题

1. 连接失败

问题表现

Connection refused
Connection reset
Socket closed

诊断步骤

# 1. 检查服务状态
systemctl status rabbitmq-server

# 2. 检查端口监听
netstat -tlnp | grep 5672
# 或
ss -tlnp | grep 5672

# 3. 检查防火墙
iptables -L -n | grep 5672
firewall-cmd --list-ports

# 4. 检查日志
tail -100 /var/log/rabbitmq/rabbit@hostname.log

# 5. 测试连接
telnet localhost 5672

解决方案

# 服务未启动
systemctl start rabbitmq-server

# 端口被防火墙阻止
firewall-cmd --permanent --add-port=5672/tcp
firewall-cmd --reload

# 监听地址问题
# 检查 rabbitmq.conf
# listeners.tcp.default = 0.0.0.0:5672

2. 认证失败

问题表现

ACCESS_REFUSED - Login was refused
Authentication failed

诊断步骤

# 1. 检查用户是否存在
rabbitmqctl list_users

# 2. 检查用户权限
rabbitmqctl list_user_permissions <username>

# 3. 检查虚拟主机
rabbitmqctl list_vhosts

解决方案

# 用户不存在
rabbitmqctl add_user myuser mypassword
rabbitmqctl set_permissions -p / myuser ".*" ".*" ".*"

# 密码错误
rabbitmqctl change_password myuser newpassword

# guest 用户远程访问
# 默认 guest 只能本地访问,需要创建新用户

3. 内存告警

问题表现

memory alarm triggered
publishers blocked

诊断步骤

# 1. 检查内存使用
rabbitmqctl status | grep -A 10 memory

# 2. 检查队列内存
rabbitmqctl list_queues name messages memory

# 3. 查看告警状态
rabbitmqctl list_local_alarms

解决方案

# 临时清理队列
rabbitmqctl purge_queue <queue_name>

# 调整内存阈值(rabbitmq.conf)
# vm_memory_high_watermark.relative = 0.6

# 启用惰性队列减少内存使用
rabbitmqctl set_policy lazy-queues ".*" \
  '{"queue-mode":"lazy"}' --apply-to queues

4. 磁盘告警

问题表现

disk alarm triggered
publishers blocked

诊断步骤

# 1. 检查磁盘使用
df -h /var/lib/rabbitmq

# 2. 检查磁盘阈值配置
rabbitmqctl environment | grep disk_free_limit

# 3. 查看大文件
du -sh /var/lib/rabbitmq/mnesia/*

解决方案

# 清理日志
> /var/log/rabbitmq/rabbit@hostname.log

# 清理旧数据
rabbitmqctl force_gc

# 调整磁盘阈值(rabbitmq.conf)
# disk_free_limit.absolute = 1GB

# 迁移数据目录
# 1. 停止 RabbitMQ
# 2. 移动 /var/lib/rabbitmq 到大磁盘
# 3. 创建软链接

5. 消息堆积

问题表现

队列消息持续增长
消费者处理不过来

诊断步骤

# 1. 查看队列消息数
rabbitmqctl list_queues name messages messages_ready messages_unacknowledged

# 2. 查看消费者数量
rabbitmqctl list_queues name consumers

# 3. 查看消费速率
rabbitmqctl list_queues name message_stats.deliver_get.rate

# 4. 查看消费者状态
rabbitmqctl list_consumers

解决方案

// 增加消费者并发
@RabbitListener(queues = "my.queue", concurrency = "5-10")
public void handle(String message) {
    // 处理消息
}
# 配置预取数量
spring:
  rabbitmq:
    listener:
      simple:
        prefetch: 50
        concurrency: 5
        max-concurrency: 10

6. 消息丢失

诊断步骤

# 1. 检查队列持久化
rabbitmqctl list_queues name durable

# 2. 检查消息确认
rabbitmqctl list_queues name messages_unacknowledged

# 3. 检查死信队列
rabbitmqctl list_queues name arguments | grep dead-letter

解决方案

// 1. 确保队列持久化
@Bean
public Queue durableQueue() {
    return QueueBuilder.durable("my.queue").build();
}

// 2. 确保消息持久化
rabbitTemplate.convertAndSend(exchange, routingKey, message, msg -> {
    msg.getMessageProperties().setDeliveryMode(MessageDeliveryMode.PERSISTENT);
    return msg;
});

// 3. 启用发布确认
spring.rabbitmq.publisher-confirm-type=correlated
spring.rabbitmq.publisher-returns=true

// 4. 手动消费确认
spring.rabbitmq.listener.simple.acknowledge-mode=manual

7. 节点无法加入集群

问题表现

Error: unable to perform an operation on node
{:timeout, 60000}

诊断步骤

# 1. 检查 Erlang Cookie
cat /var/lib/rabbitmq/.erlang.cookie

# 2. 检查主机名解析
ping rabbitmq-node1
cat /etc/hosts

# 3. 检查防火墙
# 需要开放 4369, 25672 端口
netstat -tlnp | grep 4369
netstat -tlnp | grep 25672

解决方案

# 1. 同步 Erlang Cookie
scp /var/lib/rabbitmq/.erlang.cookie root@node2:/var/lib/rabbitmq/
chmod 400 /var/lib/rabbitmq/.erlang.cookie

# 2. 配置 hosts
echo "192.168.1.101 rabbitmq-node1" >> /etc/hosts
echo "192.168.1.102 rabbitmq-node2" >> /etc/hosts

# 3. 开放端口
firewall-cmd --permanent --add-port=4369/tcp
firewall-cmd --permanent --add-port=25672/tcp
firewall-cmd --reload

# 4. 重置并加入集群
rabbitmqctl stop_app
rabbitmqctl reset
rabbitmqctl join_cluster rabbit@rabbitmq-node1
rabbitmqctl start_app

🔧 诊断工具

rabbitmq-diagnostics

# 完整诊断报告
rabbitmq-diagnostics status

# 网络检查
rabbitmq-diagnostics check_port_connectivity

# TLS 检查
rabbitmq-diagnostics cipher_suites

# 证书检查
rabbitmq-diagnostics check_certificate_expiration

日志分析

# 查看错误日志
grep -i error /var/log/rabbitmq/rabbit@hostname.log

# 查看警告
grep -i warning /var/log/rabbitmq/rabbit@hostname.log

# 实时监控日志
tail -f /var/log/rabbitmq/rabbit@hostname.log

# 启用详细日志(rabbitmq.conf)
# log.file.level = debug

管理 API

# 节点健康
curl -u admin:password http://localhost:15672/api/healthchecks/node

# 队列详情
curl -u admin:password http://localhost:15672/api/queues/%2f/my.queue

# 连接详情
curl -u admin:password http://localhost:15672/api/connections

📊 性能诊断

吞吐量分析

# 查看消息速率
rabbitmqctl list_queues name message_stats.publish.rate message_stats.deliver_get.rate

# 查看连接吞吐
rabbitmqctl list_connections name recv_oct_details.rate send_oct_details.rate

延迟分析

@Slf4j
@Component
public class LatencyMonitor {
    
    @RabbitListener(queues = "monitor.queue")
    public void checkLatency(Message message) {
        Long publishTime = (Long) message.getMessageProperties()
                .getHeaders().get("publish_time");
        
        if (publishTime != null) {
            long latency = System.currentTimeMillis() - publishTime;
            if (latency > 1000) {
                log.warn("⚠️ 高延迟: {}ms", latency);
            }
        }
    }
}

📝 本章小结

常见问题速查表

问题关键诊断命令常见原因
连接失败netstat -tlnp服务未启动/防火墙
认证失败rabbitmqctl list_users用户/密码错误
内存告警rabbitmqctl status消息堆积
磁盘告警df -h磁盘空间不足
消息堆积rabbitmqctl list_queues消费者不足
集群问题rabbitmqctl cluster_statusCookie/网络

排查流程

1. 检查服务状态
   ↓
2. 检查日志错误
   ↓
3. 检查资源使用(内存/磁盘)
   ↓
4. 检查网络连接
   ↓
5. 检查配置参数
   ↓
6. 必要时重启服务

下一步

掌握故障排查后,让我们学习 版本升级