RocketMQ常见问题排查思路

RocketMQ消费者订阅了tag,需要注意什么?

 在RocketMQ中,一个消费组能同时订阅多个 tag,但一个消费组的不同消费者不能分开订阅不同的tag,即同一个消费组的订阅关系必须保持一样。例如:常见错误使用方式同一个项目中,一段消费代码订阅tagA,然后拷贝到这段代码再更改为tagB。

 正确用法:

public void subscribe(){
      DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("arch_online_test_consumer");
      consumer.subscribe("arch_online_test","tag1 || tag2 || tag3");
}  

错误用法:

public class SubscribeTest {
  public void subscribeA(){
    DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("arch_online_test_consumer");
    consumer.subscribe("arch_online_test","tag1");
  } 

  public void subscribeB(){
    DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("arch_online_test_consumer");
    consumer.subscribe("arch_online_test","tag2");
  } 
}

 

发现大量的RocketMQ client 大量的info日志输出,如何禁用?

答: 尝试以下设置,项目中使用了Slf4j 

 

1、可以配置RocketmqClient的logger设置优先级为warn

 

2、也可以通过-Drocketmq.client.logUseSlf4j=false 和 -Drocketmq.client.logLevel=WARN 关闭MQ客户端使用Slf4j并提高日志等级

 

项目中没有使用Slf4j,可以通过-Drocketmq.client.logLevel=WARN调高日志等级。

我的服务消费后需要调用第三方接口,别人的接口调用有限制,Rocketmq消费可以限流吗?

 RocketMQ本身没有类似每秒消费多少条数据的精确限流,我们可以结合Sentienl来实现:

 private String KEY = "arch_topic:melon_consumer"; // 资源名称由topic和消费组构成
    
    public static void main(String[] args) throws InterruptedException, MQClientException {
        initFlowControlRule(); // Sentinel流控规则
        DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("arch_consumer");
        consumer.setNamesrvAddr("localhost:9876");
        consumer.subscribe("arch_topic", "*");
        consumer.setConsumeFromWhere(ConsumeFromWhere.CONSUME_FROM_FIRST_OFFSET);
        consumer.registerMessageListener(new MessageListenerConcurrently() {
            @Override
            public ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> msgs, ConsumeConcurrentlyContext context) {
                for (MessageExt msg : msgs) {
                    Entry entry = null;
                    try {
                        ContextUtil.enter(KEY); // 定义资源
                        entry = SphU.entry(KEY, EntryType.OUT);
                        System.out.printf("%s Receive New Messages: %s %n", Thread.currentThread().getName(), msg);
                    } catch (BlockException ex) {
                        // Blocked.被限流后消息重试
                        return ConsumeConcurrentlyStatus.RECONSUME_LATER;
                    } finally {
                        if (entry != null) {
                            entry.exit();
                        }
                        ContextUtil.exit();
                    }
                }
                return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
            }
        });
        consumer.start();
        System.out.printf("Consumer Started.%n");
    }

    private static void initFlowControlRule() {
        FlowRule rule = new FlowRule();
        rule.setResource(KEY);
        rule.setCount(5);// 每秒通过5条消息
        rule.setGrade(RuleConstant.FLOW_GRADE_QPS);
        rule.setControlBehavior(RuleConstant.CONTROL_BEHAVIOR_RATE_LIMITER);
        rule.setMaxQueueingTimeMs(5 * 1000); // 排队超时时间5秒
        FlowRuleManager.loadRules(Collections.singletonList(rule));
    }

 RocketMQ默认延迟等级有18个,我可以扩增吗?

 可以的,但是不建议扩增太多等级,可以通过修改broker属性messageDelayLevel来实现,注意修改了后需要重启broker.

Caused by: com.alibaba.rocketmq.remoting.exception.RemotingTimeoutException: wait response on the channel <10.58.218.151:10910> timeout, 3000(ms)

1、这个错误生产者发送消息的出现比较频繁,发送超时;

2、业务层面:

     先确认是一台机器有问题,还是多台有问题; 如果是只有一台有问题,大概率是业务服务或者机器有问题;

     确认同时间段内段其它远程服务是否异常:比如dubbo调用、zookeeper调用、mysql调用等;

 

     服务层面: 查看时间段内的JVM相关指标,比如GC次数、GC耗时等

    机器层面: 通过falcon查看机器的load、cpu busy、disk io util、swap等

 

3、MQ层面:

    如果发生的时间段,很多业务出现该情况,大概率是Broker问题

    查看机器等load、cpu busy、disk io util 、swap、net等; 

 

公司目前没有捕捉网络抖动的工具和平台,如果真发生的网络层面的抖动,是很难排查的. 下面就是一个经典的案例:

 

【图片】

Not found the consumer group consumer stats, because return offset table is empty, maybe the consumer not consume any message

检查生产者、消费者使用客户端版本是否相同

 

新增消费组,消费起点如何设置

通过设置consumer.setConsumerFromWhere属性可以解决, 注意此属性是有在group第一次消费时生效,后续都是延续上一次消费进度offset进行消费.

 

消费被订阅了, 但是没有消费(offset过小)

根据messageID进行查询具体某个消息时, 会出现以上提示. 说明当前消息还没有被某个group消费,并且当前消息offset小于maxoffset时,会有以上提示

确认是不是顺序消费,消费失败阻塞后续消费了?

 

producer运行起来发送消息时抛出异常: No route info of this topic

1、broker上不存在该topic, 创建该topic即可; 注意: 测试环境topic都是自动创建, 偶尔会出现创建异常情况, 使用手动创建保证创建成功;

2、broker没有正确链接到name server上;

3、producer没有正确连接到name server上;

 

最佳实践建议

1、消费端幂等性验证

      rocketmq无法避免消息重复(Exactly-Once). 建议采取消息Id、业务唯一标识字段做幂等性

2、消费速度慢处理方式

     1) 提高消费并行度

     2) 跳过非重要消息

     3) 优化消息消费过程

3、其它

     1) 订阅组与topic多对一, 避免一对多

     2) 顺序消息注意异常处理, 使用ack方式替代

     3) 不建议阻塞监听器, 会导致阻塞线程池, 并最终线程池耗尽无法消费

     4) 3.x版本消息重塑需要停止该group左右消费者应用, 否则不生效

     5) 建议使用push模式, 没有特殊需求属性值尽量保证保持默认配置

 

================

控制台查询message,messageTrack提示 xxxgroup订阅了,但是被过滤掉了

比较消息和group的tag是否匹配。注意*的问题,只有在配置group订阅的时候*才有全匹配的意思,在消息在只是表示tag是字符串"*"

控制台查询message,messageTrack提示 xxxgroup订阅了,但是没有消费(Offset小)

表示还没有消费到这一条,可以查询消费者进度对比核实下

消息没有被消费。

先根据msgid查询消息所在queue和对应偏移,然后查询消费者偏移比较大小。如果消费者偏移小,说明还没消费。如果消费者偏移大说明已经越过,可以通过tag判断。也有可能是消费失败但是消费者没有记录异常。

消费者无法正常消费

消费者启动但是一直有积压无法消费可能有一下几种可能

通group订阅信息不一致导致:详情  同group订阅不同topic 无法消费的问题

InstanceName  冲突:InstanceName是队列负载均衡算法的计算依赖,相同的InstanceName会导致队列分配混乱导致部分队列消费混乱

多起的的客户端:经常发现业务在项目中多起了客户端,可以先查询客户端连接然后与业务核实

消费队列不均匀

指观察消费队列 一部分差值正常,一部分差值不正常有很多挤压。

先观察消费者队列的分配情况,之后观察消费者连接的InstanceName 是否有冲突

 

异常:RemotingTimeoutException: wait response on the channel <xxx> timeout, xxx(ms)

这个异常本质就是rpc调用超时,rocket的rpc请求都是异步的。这个异常表示发送成功后等待响应超过了最大等待时间。可能是有人发送了大消息,或者rocket发生了波动。可以适当的调整最大超时时间  setSendMsgTimeout 

 

===================

 

 

上图中,一个 MQ 对应有两个消费者,他们是在同一个 Group1 中,起初大家都只有 Topic1,这时候是正常消费的。

 

但如果在第一个消费者里面加入一个 Topic2,这时候是无法消费或消费不正常了。

 

这是 RocketMQ 本身的机制引起的问题,需要在第二个消费者里面加入 Topic2 才能正常消费。

 

 为什么

因为broker 端通过group的订阅配置来构建consumeQueue 。

group 订阅配置存储在

com.alibaba.rocketmq.broker.BrokerController的SubscriptionGroupManager属性中

消费消费者启动后会定时发送心跳,心跳中会带上自己的订阅配置

private void sendHeartbeatToAllBroker() {
    final HeartbeatData heartbeatData = this.prepareHeartbeatData();
    final boolean producerEmpty = heartbeatData.getProducerDataSet().isEmpty();
    final boolean consumerEmpty = heartbeatData.getConsumerDataSet().isEmpty();
    if (producerEmpty && consumerEmpty) {
        log.warn("sending hearbeat, but no consumer and no producer");
        return;
    }
 
    Iterator<Entry<String, HashMap<Long, String>>> it = this.brokerAddrTable.entrySet().iterator();
    while (it.hasNext()) {
        Entry<String, HashMap<Long, String>> entry = it.next();
        String brokerName = entry.getKey();
        HashMap<Long, String> oneTable = entry.getValue();
        if (oneTable != null) {
            for (Long id : oneTable.keySet()) {
                String addr = oneTable.get(id);
                if (addr != null) {
                    if (consumerEmpty) {
                        if (id != MixAll.MASTER_ID)
                            continue;
                    }
 
                    try {
                        this.mQClientAPIImpl.sendHearbeat(addr, heartbeatData, 3000);
                        log.info("send heart beat to broker[{} {} {}] success", brokerName, id, addr);
                        log.info(heartbeatData.toString());
                    } catch (Exception e) {
                        log.error("send heart beat to broker exception", e);
                    }
                }
            }
        }
    }
}
 
public class HeartbeatData extends RemotingSerializable {
    private String clientID;
    private Set<ProducerData> producerDataSet = new HashSet<ProducerData>();
    //消费者组的订阅配置
    private Set<ConsumerData> consumerDataSet = new HashSet<ConsumerData>();
}

 

心跳请求code为

RequestCode.HEART_BEAT = 34

broker端处理请求时会查询订阅配置和当前,是否不一致需要更新。

所以在上面的情况上,两个客户端会交替更新broker的订阅信息,而topic1怎么更新都是存在的,但是topic2则会收影响导致消费有问题

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值