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则会收影响导致消费有问题