微服务框架下,因发送端与消费端的vhost不一致,导致rabbitmq出现严重的消息堆积

一、背景

在生产环境下,rabbitmq机器出现磁盘空间不足的报警,发现是某个队列的消息只有生产,迟迟没有消费。


可以得到的信息是:

  • 队列queue是data_center_file_change_queue
  • 队列绑定的交换机是resourceChangeExchange,见下图
  • 队列所在的vhost是/

之所以出现rabbitmq消息堆积,是因为消费程序所在的vhost与消息生产者所在的vhost不是同一个。

下面将逐步说明其排查过程,以及解决方法。

二、服务架构

在微服务框架下,两个服务之间,通过rabbitmq来解耦。

三、问题排查

消息没有被消费,首先怀疑的一点是:发送方或者消费方修改了消息体,因为出现异常,导致消费失败,从而重新放入了队列。

1、尝试解码消息内容

base64解密工具,输入上面的消息体,进行解密。

2、对比分析生产者和消费者的java代码

近期没有修改过报文体,而且我看消费者的代码,都有对异常进行捕获。

也就是说,消费端无论是消费成功还是失败,该消息就算在出现异常的时候,也会被视为已消费。(并不会出现消息堆积)

3、生产和消费的双方是否处于同一个vhost

检索队列名称,发现同样的队列名,在不同的vhost都存在。详见下图:

进一步查看程序的配置,得知消息生产程序并未指定vhost,默认使用的是/

而消息消费程序,指定了vhost,如此导致两边的vhost不一致。

这是导致rabbitmq出现严重的消息堆积的根源。

4、保证程序的vhost一致

  • 消费端程序配置的vhost

修改前:

修改后:

  • 生产端程序配置的vhost

四、验证

经过一段时间,看到堆积的消息正被慢慢消费掉。

最后积压的消息全部被消费成功。

五、程序的代码示例

1、消息生产者

java 复制代码
@Configuration
public class RabbitConfig {
    @Bean
    public FanoutExchange resourceChangeExchange() {
        return new FanoutExchange("resourceChangeExchange");
    }
}
java 复制代码
    @Resource(name = "rabbitTemplate")
    private AmqpTemplate rabbitTemplate;
    
	Map<String, Object> map = Maps.newHashMap();
    map.put("xxxx", "");
        
    this.rabbitTemplate.convertAndSend("resourceChangeExchange", "", map);

2、消息消费者

java 复制代码
    public interface RabbitMQ {
        /**
         * 资源中心文件变动队列 名 (变动事件)
         */
        String DATA_CENTER_FILE_CHANGE_QUEUE = "data_center_file_change_queue";

        /**
         * 资源中心文件变动队列 名 (变动事件) Direct交换机名
         */
        String DATA_CENTER_FILE_CHANGE_DIRECT_EXCHANGE = "resourceChangeExchange";
   }
java 复制代码
@Configuration
public class RabbitMqConfig {
    @Bean
    public Queue dataCenterFileChangeQueue() {
        return new Queue(RabbitMQ.DATA_CENTER_FILE_CHANGE_QUEUE, true, false, false, null);
    }
    
    @Bean
    public FanoutExchange dataCenterFileChangeDirectExchange() {
        return new FanoutExchange(RabbitMQ.DATA_CENTER_FILE_CHANGE_DIRECT_EXCHANGE, true, false);
    }
}
  • 消费mq消息
java 复制代码
    @RabbitListener(queues = RabbitMQ.DATA_CENTER_FILE_CHANGE_QUEUE)
    public void listener(Map<String, String> messageMap, Message message, Channel channel) {
	// messageMap 是mq消息体
}
相关推荐
IT专业服务商19 分钟前
联想 SR550 服务器,配置 RAID 5教程!
运维·服务器·windows·microsoft·硬件架构
Clf丶忆笙21 分钟前
SpringBoot异步处理@Async深度解析:从基础到高阶实战
spring boot
小小工匠2 小时前
架构思维:通用架构模式_系统监控的设计
架构·系统监控
小马爱打代码2 小时前
面试题 - Kafka、RabbitMQ、RocketMQ如何选型?
kafka·rabbitmq·rocketmq
Bruk.Liu2 小时前
Kafka、RabbitMQ 和 RocketMQ区别及上手难度
kafka·rabbitmq·rocketmq
佩奇的技术笔记2 小时前
Java学习手册:微服务设计原则
java·微服务
柯南二号2 小时前
【后端】SpringBoot用CORS解决无法跨域访问的问题
java·spring boot·后端
martian6652 小时前
信创生态核心技术栈:数据库与中间件
开发语言·中间件·系统架构·系统安全·创业创新
Bruk.Liu3 小时前
Linux 上安装RabbitMQ
linux·服务器·rabbitmq
UpUpUp……3 小时前
Linux--JsonCpp
linux·运维·服务器·c++·笔记·json