当前位置:   article > 正文

RocketMq 部分队列不能消费问题排查_rocketmq不消费原因

rocketmq不消费原因

事件回放

晚上8:40左右,测试反馈测试环境的业务不正常,经过排查,发送MQ都没有收到,但是生产者那边的MQ确实已经发出来了,rocketMq的控制台也能查到对应的这条消息。

第一条发现:

在查看消息详情的时候,发现这条消息对应的consumer的TREAD_TYPE NOT_CONSUMER_YET , NOT_CONSUMER_YET 表示这条消息确确实实存在,但是没有被消费。

没有被消费的情况有很多种, 比如说:代码里面出现了异常,没有正确的返回消费状态,那么这条消息可能就不会被消费。 还有就是消费组有问题,队列没有正常分配。

排查过程

通过查看日志代码,没有明显的Excepetion抛出来,但是接收MQ的日志完全没有打出来,继续查看rocketMq 控制台的消费组信息。

发现了一个非常诡异的地方,一个clientId对应了两个clientAddr , 理论上来说,这是不可能的,因为代码里面只启动了一个消费者.

查看队列消费状态

从图中可以看到,消费组有两个实例,但是队列分配的时候,有一半的队列没有分配到处理实例,所以问题出现的原因已经很明显了,就是因为有些队列没有分配到处理实例,导致消息一直没有被消费。

那么是因为什么原因导致的呢? 我们从源码的角度上看一看rocketMq的队列负载均衡策略

负载均衡算法

分配算法有下面6种,默认的是AllocateMessageQueueAveragely, 平均分配策略

平均分配策略(默认)(AllocateMessageQueueAveragely)
环形分配策略(AllocateMessageQueueAveragelyByCircle)
手动配置分配策略(AllocateMessageQueueByConfig)
机房分配策略(AllocateMessageQueueByMachineRoom)
一致性哈希分配策略(AllocateMessageQueueConsistentHash)
靠近机房策略(AllocateMachineRoomNearby)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

我们看一下AllocateMessageQueueAveragely的圆满。

consumerGroup : 消费组

currentCID:当前实例的ID

mqAll: 所有的队列

cidAll: 当前消费组里面的所有的实例

@Override
    public List<MessageQueue> allocate(String consumerGroup, String currentCID, List<MessageQueue> mqAll,
        List<String> cidAll) {
        if (currentCID == null || currentCID.length() < 1) {
            throw new IllegalArgumentException("currentCID is empty");
        }
        if (mqAll == null || mqAll.isEmpty()) {
            throw new IllegalArgumentException("mqAll is null or mqAll empty");
        }
        if (cidAll == null || cidAll.isEmpty()) {
            throw new IllegalArgumentException("cidAll is null or cidAll empty");
        }

        List<MessageQueue> result = new ArrayList<MessageQueue>();
        if (!cidAll.contains(currentCID)) {
            log.info("[BUG] ConsumerGroup: {} The consumerId: {} not in cidAll: {}",
                consumerGroup,
                currentCID,
                cidAll);
            return result;
        }
				// 重点在这个地方,通过当前实例ID,取消费组里面的第一个节点。
        int index = cidAll.indexOf(currentCID);
        // 求出每个实例平均分配的队列数
        int mod = mqAll.size() % cidAll.size();
        int averageSize =
            mqAll.size() <= cidAll.size() ? 1 : (mod > 0 && index < mod ? mqAll.size() / cidAll.size()
                + 1 : mqAll.size() / cidAll.size());
        int startIndex = (mod > 0 && index < mod) ? index * averageSize : index * averageSize + mod;
        int range = Math.min(averageSize, mqAll.size() - startIndex);
        for (int i = 0; i < range; i++) {
            result.add(mqAll.get((startIndex + i) % mqAll.size()));
        }
        return result;
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35

说明:

从我们上面的现象可以得知,一个消费组中,当CID相同的情况下,只会有第一个可以实际分配到处理队列, 但是由于每个实例分配的队列数,是通过。队列总数%实例总数得到的,这就导致了。有一个消费组实例是没有处理队列的。

问题:

是什么原因导致的一个应用启了两个实例呢? 首先怀疑的是jar冲突之类的导致程序运行不正常,但是通过依赖工具分析,没有发现异常。

于是去查看tomcat的热部署机制,因为热部署机制也是会导致应用被发布两次。

 <Host name="localhost"  appBase="webapps"
            unpackWARs="true" autoDeploy="false">
  • 1
  • 2

很遗憾,tomcat的autoDeploy是false, 这个问题排除。

接下来去webapps的文件夹里面查询运行的源代码,发现了问题所在

tomcat外面运行了一套代码,ROOT里面运行一套代码,这就导致了一个tomcat里面运行了两个应用。删除外层的应用,保留ROOT根目录, 重启tomcat ,问题解决

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/Gausst松鼠会/article/detail/712768
推荐阅读
相关标签
  

闽ICP备14008679号