[Alibaba Middleware Technology Series] "RocketMQ Technology Topic" helps you sort out RocketMQ-related consumption problems and principle analysis and summary

The problem of repeated consumption of messages

Repeated consumption of messages is one of the common problems that occur in all MQs. In some sensitive scenarios, repeated consumption will cause serious consequences, such as repeated deductions.

Scenarios and solutions for repeated message consumption

Under what circumstances will RocketMQ's repeated consumption of messages occur?

Producer repeated send scenario

When the system call link is relatively long, for example, system A calls system B, system B then sends the message to RocketMQ, when system A calls system B.

If system B processes successfully but fails to return the successful call result to system A for a long time, system A will try to re-initiate the request to system B, causing system B to process repeatedly and initiate multiple messages to RocketMQ, resulting in repeated consumption.

Consumers repeatedly send scenarios

When system B sends a message to RocketMQ, the same problem as above may also occur. The message sending times out, and as a result, system B retries, causing RocketMQ to receive repeated messages.

Consumers repeatedly send scenarios

When RocketMQ successfully receives the message and hands it over to the consumer for processing, if the consumer has not had time to submit the offset to RocketMQ after the consumption is completed, and it shuts down or restarts itself, then RocketMQ will consider the consumption to have failed if it does not receive the offset. , will resend the message to the consumer for consumption again.

The consumer does not return success immediately

A possible problem with the problem of repeated consumption: an exception was generated when the consumer consumed the message, and the CONSUME_SUCCESS flag was not returned.

Because of the message re-consumption caused by the abnormal message processing, RocketMQ can keep the message very well, and the consumption must be successful!

Official to comsumerMessage method
It is not recommend to throw exception,rather than returning ConsumeConcurrentlyStatus.RECONSUME_LATER if consumption failure
复制代码

In any case, do not throw an exception. If you need to re-consume, you can return RECONSUME_LATER to actively request re-consumption.

catch Exception root exception to capture business processing exceptions:

consumer.registerMessageListener(new MessageListenerConcurrently() {
    
    
                public ConsumeConcurrentlyStatus consumeMessage(List msgs,
                    ConsumeConcurrentlyContext context) {
    
    
                    logger.debug(Thread.currentThread().getName() + " Receive New Messages: " + msgs + "%n");
                    MessagePack msgpack = new MessagePack();
                    for (MessageExt msg : msgs){
    
    
                        byte[] data = msg.getBody();
                        try {
    
    
                            RTMsgPack rtmsg = msgpack.read(data, RTMsgPack.class);
                            logger.debug("Receive a message:" + rtmsg);
                            anlysisRTMsgPack(rtmsg, engine);
                        } catch (IOException e) {
    
    
                            logger.error("Unpack RTMsg:", e);
                        } catch (Exception e1){
    
    
                            logger.warn("Unexcepted exception.", e1);
                        }
                    }
                    logger.debug("RETURN CONSUME SUCCESS.");
                    return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
                }
 });
复制代码

Problem with setting CONSUME_FROM_LAST_OFFSET

When Consumer is consuming, he will set where to start consuming. The default is CONSUME_FROM_LAST_OFFSET, and the set value is shown in the code.

public enum ConsumeFromWhere {
    
    

    CONSUME_FROM_LAST_OFFSET,

   CONSUME_FROM_LAST_OFFSET_AND_FROM_MIN_WHEN_BOOT_FIRST,

    CONSUME_FROM_MIN_OFFSET,

    CONSUME_FROM_MAX_OFFSET,

    CONSUME_FROM_FIRST_OFFSET,

    CONSUME_FROM_TIMESTAMP,
}
复制代码
  • CONSUME_FROM_LAST_OFFSET: Start consumption from the last offset, starting from the location where the consumer last consumed.
    • If it is a new consumer, it must be judged according to the situation of the consumer group to which the client belongs.
    • If the consumer group you belong to is newly online, and the subscribed message has not been received from the earliest message, the RocketMQ designer believes that you are a new online business, and you will be forced to start consuming from the first message.
    • If the subscribed message has already generated an expired message, it will be consumed from the time when our client is started.

ConsumeFromWhere is only valid when a new consumer is started for the first time

  • CONSUME_FROM_FIRST_OFFSET: start consumption from the minimum offset,
  • CONSUME_FROM_TIMESTAMP: Start consumption from a certain time.
  • And judging whether it is a new ConsumerGroup is judged on the broker side.
  • Which offset to consume is first stored locally in the Consumer, and the consumption offset is synchronized with the broker at regular intervals.
  • When the broker is judging whether it is a new consumer group, it is to check whether there is an offset record of this consumer group on the broker side.

Offset invalidation

For a new queue, this parameter is also useless, and consumption starts from 0.

So, here is a question. I have already set CONSUME_FROM_LAST_OFFSET, why do I still consume repeatedly? Maybe you are not a new consumergroup, or it may be a new Queue.

Retry Queue and Dead Letter Queue

  • The consumer side never returns the consumption result. RocketMQ thinks that the message has not been received, and the next time the consumer pulls, the broker will still send the message.
  • Any exception should be caught and returned: ConsumeConcurrentlyStatus.RECONSUME_LATER

RocketMQ will be placed in the retry queue, TOPIC is: %RETRY%+COnsumerGroup name

  • The retried message is delivered to this ConsumerGroup again after a certain time point of delay (the default is 10 seconds, which can be set by the business).
  • And if repeated consumption continues to fail for a certain number of times (16 times by default), it will be delivered to the DLQ dead letter queue, and manual intervention is required at this time.

private int consumeMessageBatchMaxSize = 1;

private int pullBatchSize = 32;
复制代码
  • consumeMessageBatchMaxSize is the maximum number of batch consumption
  • pullBatchSize is the maximum number of items pulled each time

broker side

private String messageDelayLevel = "1s 5s 10s 30s 1m 2m 3m 4m 5m 6m 7m 8m 9m 10m 20m 30m 1h 2h";
复制代码

The parameter is to set the retry time, that is, after the first 1s and after the second 5s

Do not change the production environment

messageDelayLevel = 5s 5s 5s 5s 5s 5s 5s 5s 5s 5s 5s 5s 5s 5s 5s 5s 5s 5s
复制代码

After 16 times, there is an additional topic named: %DLQ%+consumergroup

The default 16 times can be changed, but it can only be modified by using DefaultMQPullConsumer.

DefaultMQPushConsumer cannot modify this value.

consumeMessageBatchMaxSize This size is the number of messages processed by the callback listener registered by the consumer at one time, the default is 1, not the number of messages pulled each time (the default is 32), this should not be confused.

Update of message consumption progress

Future articles will introduce the functions and analysis of related progress updates

share resources

Information sharing
To obtain the above resources, please visit the open source project and click to jump

Guess you like

Origin blog.csdn.net/star20100906/article/details/132310450