微服务框架下,因发送端与消费端的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消息体
}
相关推荐
一只栖枝2 小时前
华为 HCIE 大数据认证中 Linux 命令行的运用及价值
大数据·linux·运维·华为·华为认证·hcie·it
wuicer4 小时前
ubuntu 20.04 安装anaconda以及安装spyder
linux·运维·ubuntu
IT毕设实战小研4 小时前
基于Spring Boot 4s店车辆管理系统 租车管理系统 停车位管理系统 智慧车辆管理系统
java·开发语言·spring boot·后端·spring·毕业设计·课程设计
一只爱撸猫的程序猿5 小时前
使用Spring AI配合MCP(Model Context Protocol)构建一个"智能代码审查助手"
spring boot·aigc·ai编程
甄超锋5 小时前
Java ArrayList的介绍及用法
java·windows·spring boot·python·spring·spring cloud·tomcat
小晶晶京京6 小时前
day34-LNMP详解
linux·运维·服务器
喂完待续6 小时前
Apache Hudi:数据湖的实时革命
大数据·数据仓库·分布式·架构·apache·数据库架构
fengyehongWorld7 小时前
Linux crontab定时任务
linux·运维
碎像7 小时前
Linux上配置环境变量
linux·运维·服务器
武昌库里写JAVA8 小时前
JAVA面试汇总(四)JVM(一)
java·vue.js·spring boot·sql·学习