異常現象
監控日誌展示如下:
[2019-10-30 14:31:23.339 INFO ] [ConsumeMessageThread_7] (com.xxx.service.mq.MQConsumerService:93) - 消費消息:msgId=0A064C3E000179C63692734B339201B0 topic=topic_xxx tag=yyy reconsumeTimes=12
reconsumeTimes 代表消費重試次數。
同時日誌中頻繁顯示同一條msgId。
排查原因
通過debug發現在執行業務代碼時,拋出的異常被mq捕獲,如下:
org.apache.rocketmq.client.impl.consumer.ConsumeMessageOrderlyService.ConsumeRequest#run
try {
this.processQueue.getLockConsume().lock();
if (this.processQueue.isDropped()) {
log.warn("consumeMessage, the message queue not be able to consume, because it's dropped. {}",
this.messageQueue);
break;
}
status = messageListener.consumeMessage(Collections.unmodifiableList(msgs), context);
} catch (Throwable e) {
log.warn("consumeMessage exception: {} Group: {} Msgs: {} MQ: {}", //
RemotingHelper.exceptionSimpleDesc(e), //
ConsumeMessageOrderlyService.this.consumerGroup, //
msgs, //
messageQueue);
hasException = true;
} finally {
this.processQueue.getLockConsume().unlock();
}
if (null == status) {
status = ConsumeOrderlyStatus.SUSPEND_CURRENT_QUEUE_A_MOMENT;
}
當發生異常時messageListener.consumeMessage方法的返回值爲null
當status爲null時,status會被賦值爲ConsumeOrderlyStatus.SUSPEND_CURRENT_QUEUE_A_MOMENT狀態(即隔一段時間後重試)。
上文造成消息重試的異常信息如下
java.lang.NoSuchMethodError: com.soaclient.dto.xxx.LogQueryDto.getMqTypeIn()Ljava/util/List;
由於該異常爲Error級別,而業務代碼異常捕獲級別爲Exception,導致異常沒有被捕獲。
改進方法
1、修改業務代碼異常
2、修改異常捕獲級別爲throwable級別,並輸出異常日誌
3、增加重試次數限制,當大於一定次數時,則不再重試。
如果不加重試,針對順序消費的情況,可能會出現消費被阻塞的情況。
而無序消費,默認最多重試16次。
題外話
當爲無序消費時,代碼如下:
try {
ConsumeMessageConcurrentlyService.this.resetRetryTopic(msgs);
if (msgs != null && !msgs.isEmpty()) {
for (MessageExt msg : msgs) {
MessageAccessor.setConsumeStartTimeStamp(msg, String.valueOf(System.currentTimeMillis()));
}
}
status = listener.consumeMessage(Collections.unmodifiableList(msgs), context);
} catch (Throwable e) {
log.warn("consumeMessage exception: {} Group: {} Msgs: {} MQ: {}",
RemotingHelper.exceptionSimpleDesc(e), //
ConsumeMessageConcurrentlyService.this.consumerGroup,
msgs,
messageQueue);
hasException = true;
}
if (null == status) {
log.warn("consumeMessage return null, Group: {} Msgs: {} MQ: {}",
ConsumeMessageConcurrentlyService.this.consumerGroup,
msgs,
messageQueue);
status = ConsumeConcurrentlyStatus.RECONSUME_LATER;
}
參考
消息隊列 MQ > 高級特性 > 消息重試
https://help.aliyun.com/document_detail/43490.html?spm=a2c4g.11186623.6.555.35b96c99oNQKPt