聊聊rocketmq的ConsumeMode.CONCURRENTLY

编程

本文主要研究一下rocketmq的ConsumeMode.CONCURRENTLY

ConsumeMode.CONCURRENTLY

rocketmq-spring-boot-2.0.4-sources.jar!/org/apache/rocketmq/spring/annotation/ConsumeMode.java

public enum ConsumeMode {

/**

* Receive asynchronously delivered messages concurrently

*/

CONCURRENTLY,

/**

* Receive asynchronously delivered messages orderly. one queue, one thread

*/

ORDERLY

}

  • ConsumeMode定义了CONCURRENTLY、ORDERLY两个枚举值

DefaultRocketMQListenerContainer

rocketmq-spring-boot-2.0.4-sources.jar!/org/apache/rocketmq/spring/support/DefaultRocketMQListenerContainer.java

public class DefaultRocketMQListenerContainer implements InitializingBean,

RocketMQListenerContainer, SmartLifecycle, ApplicationContextAware {

//......

private void initRocketMQPushConsumer() throws MQClientException {

Assert.notNull(rocketMQListener, "Property "rocketMQListener" is required");

Assert.notNull(consumerGroup, "Property "consumerGroup" is required");

Assert.notNull(nameServer, "Property "nameServer" is required");

Assert.notNull(topic, "Property "topic" is required");

RPCHook rpcHook = RocketMQUtil.getRPCHookByAkSk(applicationContext.getEnvironment(),

this.rocketMQMessageListener.accessKey(), this.rocketMQMessageListener.secretKey());

boolean enableMsgTrace = rocketMQMessageListener.enableMsgTrace();

if (Objects.nonNull(rpcHook)) {

consumer = new DefaultMQPushConsumer(consumerGroup, rpcHook, new AllocateMessageQueueAveragely(),

enableMsgTrace, this.applicationContext.getEnvironment().

resolveRequiredPlaceholders(this.rocketMQMessageListener.customizedTraceTopic()));

consumer.setVipChannelEnabled(false);

consumer.setInstanceName(RocketMQUtil.getInstanceName(rpcHook, consumerGroup));

} else {

log.debug("Access-key or secret-key not configure in " + this + ".");

consumer = new DefaultMQPushConsumer(consumerGroup, enableMsgTrace,

this.applicationContext.getEnvironment().

resolveRequiredPlaceholders(this.rocketMQMessageListener.customizedTraceTopic()));

}

String customizedNameServer = this.applicationContext.getEnvironment().resolveRequiredPlaceholders(this.rocketMQMessageListener.nameServer());

if (customizedNameServer != null) {

consumer.setNamesrvAddr(customizedNameServer);

} else {

consumer.setNamesrvAddr(nameServer);

}

if (accessChannel != null) {

consumer.setAccessChannel(accessChannel);

}

consumer.setConsumeThreadMax(consumeThreadMax);

if (consumeThreadMax < consumer.getConsumeThreadMin()) {

consumer.setConsumeThreadMin(consumeThreadMax);

}

consumer.setConsumeTimeout(consumeTimeout);

switch (messageModel) {

case BROADCASTING:

consumer.setMessageModel(org.apache.rocketmq.common.protocol.heartbeat.MessageModel.BROADCASTING);

break;

case CLUSTERING:

consumer.setMessageModel(org.apache.rocketmq.common.protocol.heartbeat.MessageModel.CLUSTERING);

break;

default:

throw new IllegalArgumentException("Property "messageModel" was wrong.");

}

switch (selectorType) {

case TAG:

consumer.subscribe(topic, selectorExpression);

break;

case SQL92:

consumer.subscribe(topic, MessageSelector.bySql(selectorExpression));

break;

default:

throw new IllegalArgumentException("Property "selectorType" was wrong.");

}

switch (consumeMode) {

case ORDERLY:

consumer.setMessageListener(new DefaultMessageListenerOrderly());

break;

case CONCURRENTLY:

consumer.setMessageListener(new DefaultMessageListenerConcurrently());

break;

default:

throw new IllegalArgumentException("Property "consumeMode" was wrong.");

}

if (rocketMQListener instanceof RocketMQPushConsumerLifecycleListener) {

((RocketMQPushConsumerLifecycleListener) rocketMQListener).prepareStart(consumer);

}

}

}

  • initRocketMQPushConsumer方法对于consumeMode为CONCURRENTLY则设置的messageListener为DefaultMessageListenerConcurrently

DefaultMessageListenerConcurrently

rocketmq-spring-boot-2.0.4-sources.jar!/org/apache/rocketmq/spring/support/DefaultRocketMQListenerContainer.java

    public class DefaultMessageListenerConcurrently implements MessageListenerConcurrently {

@SuppressWarnings("unchecked")

@Override

public ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> msgs, ConsumeConcurrentlyContext context) {

for (MessageExt messageExt : msgs) {

log.debug("received msg: {}", messageExt);

try {

long now = System.currentTimeMillis();

rocketMQListener.onMessage(doConvertMessage(messageExt));

long costTime = System.currentTimeMillis() - now;

log.debug("consume {} cost: {} ms", messageExt.getMsgId(), costTime);

} catch (Exception e) {

log.warn("consume message failed. messageExt:{}", messageExt, e);

context.setDelayLevelWhenNextConsume(delayLevelWhenNextConsume);

return ConsumeConcurrentlyStatus.RECONSUME_LATER;

}

}

return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;

}

}

  • DefaultMessageListenerConcurrently实现了MessageListenerConcurrently接口,其consumeMessage方法遍历msgs,然后挨个调用rocketMQListener.onMessage(doConvertMessage(messageExt)),若循环中出现异常被捕获住了会执行context.setDelayLevelWhenNextConsume(delayLevelWhenNextConsume),然后返回ConsumeConcurrentlyStatus.RECONSUME_LATER

ConsumeMessageConcurrentlyService

rocketmq-client-4.6.0-sources.jar!/org/apache/rocketmq/client/impl/consumer/ConsumeMessageConcurrentlyService.java

    class ConsumeRequest implements Runnable {

private final List<MessageExt> msgs;

private final ProcessQueue processQueue;

private final MessageQueue messageQueue;

public ConsumeRequest(List<MessageExt> msgs, ProcessQueue processQueue, MessageQueue messageQueue) {

this.msgs = msgs;

this.processQueue = processQueue;

this.messageQueue = messageQueue;

}

public List<MessageExt> getMsgs() {

return msgs;

}

public ProcessQueue getProcessQueue() {

return processQueue;

}

@Override

public void run() {

if (this.processQueue.isDropped()) {

log.info("the message queue not be able to consume, because it"s dropped. group={} {}", ConsumeMessageConcurrentlyService.this.consumerGroup, this.messageQueue);

return;

}

MessageListenerConcurrently listener = ConsumeMessageConcurrentlyService.this.messageListener;

ConsumeConcurrentlyContext context = new ConsumeConcurrentlyContext(messageQueue);

ConsumeConcurrentlyStatus status = null;

defaultMQPushConsumerImpl.resetRetryAndNamespace(msgs, defaultMQPushConsumer.getConsumerGroup());

ConsumeMessageContext consumeMessageContext = null;

if (ConsumeMessageConcurrentlyService.this.defaultMQPushConsumerImpl.hasHook()) {

consumeMessageContext = new ConsumeMessageContext();

consumeMessageContext.setNamespace(defaultMQPushConsumer.getNamespace());

consumeMessageContext.setConsumerGroup(defaultMQPushConsumer.getConsumerGroup());

consumeMessageContext.setProps(new HashMap<String, String>());

consumeMessageContext.setMq(messageQueue);

consumeMessageContext.setMsgList(msgs);

consumeMessageContext.setSuccess(false);

ConsumeMessageConcurrentlyService.this.defaultMQPushConsumerImpl.executeHookBefore(consumeMessageContext);

}

long beginTimestamp = System.currentTimeMillis();

boolean hasException = false;

ConsumeReturnType returnType = ConsumeReturnType.SUCCESS;

try {

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;

}

long consumeRT = System.currentTimeMillis() - beginTimestamp;

if (null == status) {

if (hasException) {

returnType = ConsumeReturnType.EXCEPTION;

} else {

returnType = ConsumeReturnType.RETURNNULL;

}

} else if (consumeRT >= defaultMQPushConsumer.getConsumeTimeout() * 60 * 1000) {

returnType = ConsumeReturnType.TIME_OUT;

} else if (ConsumeConcurrentlyStatus.RECONSUME_LATER == status) {

returnType = ConsumeReturnType.FAILED;

} else if (ConsumeConcurrentlyStatus.CONSUME_SUCCESS == status) {

returnType = ConsumeReturnType.SUCCESS;

}

if (ConsumeMessageConcurrentlyService.this.defaultMQPushConsumerImpl.hasHook()) {

consumeMessageContext.getProps().put(MixAll.CONSUME_CONTEXT_TYPE, returnType.name());

}

if (null == status) {

log.warn("consumeMessage return null, Group: {} Msgs: {} MQ: {}",

ConsumeMessageConcurrentlyService.this.consumerGroup,

msgs,

messageQueue);

status = ConsumeConcurrentlyStatus.RECONSUME_LATER;

}

if (ConsumeMessageConcurrentlyService.this.defaultMQPushConsumerImpl.hasHook()) {

consumeMessageContext.setStatus(status.toString());

consumeMessageContext.setSuccess(ConsumeConcurrentlyStatus.CONSUME_SUCCESS == status);

ConsumeMessageConcurrentlyService.this.defaultMQPushConsumerImpl.executeHookAfter(consumeMessageContext);

}

ConsumeMessageConcurrentlyService.this.getConsumerStatsManager()

.incConsumeRT(ConsumeMessageConcurrentlyService.this.consumerGroup, messageQueue.getTopic(), consumeRT);

if (!processQueue.isDropped()) {

ConsumeMessageConcurrentlyService.this.processConsumeResult(status, context, this);

} else {

log.warn("processQueue is dropped without process consume result. messageQueue={}, msgs={}", messageQueue, msgs);

}

}

public MessageQueue getMessageQueue() {

return messageQueue;

}

}

  • ConsumeRequest实现了Runnable接口,它的构造器要求传入msgs、processQueue、messageQueue参数;其run方法会执行listener.consumeMessage(Collections.unmodifiableList(msgs), context)以及processConsumeResult(status, context, this)

小结

DefaultMessageListenerConcurrently实现了MessageListenerConcurrently接口,其consumeMessage方法遍历msgs,然后挨个调用rocketMQListener.onMessage(doConvertMessage(messageExt)),若循环中出现异常被捕获住了会执行context.setDelayLevelWhenNextConsume(delayLevelWhenNextConsume),然后返回ConsumeConcurrentlyStatus.RECONSUME_LATER

ConsumeMessageConcurrentlyService.ConsumeRequest不需要自己去拉取msgs,而ConsumeMessageOrderlyService.ConsumeRequest需要自己先加锁,然后拉取msgs

doc

  • ConsumeMessageConcurrentlyService

以上是 聊聊rocketmq的ConsumeMode.CONCURRENTLY 的全部内容, 来源链接: utcz.com/z/511931.html

回到顶部