All Products
Search
Document Center

ApsaraMQ for RocketMQ:Message retry

Last Updated:Mar 11, 2026

When a consumer fails to process a message or the processing times out, ApsaraMQ for RocketMQ automatically redelivers the message based on a retry policy. After all retry attempts are exhausted, the message is moved to a dead-letter queue. You can consume messages in dead-letter queues to restore your business.

Usage notes

  • The message ID remains unchanged across all retry attempts.

  • Consumption retry works only in clustering consumption mode. In broadcasting consumption mode, failed messages are not redelivered -- the consumer skips to the next message.

How it works

A message transitions through the following states:

StateDescription
ReadyThe message is queued in the broker and available for consumption.
InflightA consumer has pulled the message and is processing it. No result has been returned yet.
WaitingRetryConsumption failed or timed out. The message waits for the retry interval to elapse before returning to the Ready state.
CommitThe consumer returned a success response. Consumption is complete.
DLQIf the feature for retaining dead-letter messages is enabled, the message is moved to the dead-letter topic after exhausting all retry attempts.
TCP message state diagram

Retry timing

The interval between two consecutive consumption attempts has three components:

Interval between attempts = consumption duration + retry interval + time in Ready state

Retry interval timing diagram

For example, assume a message waits 5 seconds in the Ready state and takes 6 seconds to process before failing. With a 10-second retry interval:

  1. 0 s -- The message enters the Ready state.

  2. 5 s -- The consumer pulls the message (Inflight).

  3. 11 s -- Consumption fails after 6 seconds. The message enters WaitingRetry.

  4. 21 s -- After the 10-second retry interval, the message returns to Ready.

  5. 26 s -- The consumer pulls the message again for the next attempt.

Total interval: 6 + 10 + 5 = 21 seconds.

Retry policies for TCP

Ordered messages

SettingDetails
Retry intervalSet with the suspendTimeMillis parameter. Valid range: 10 -- 30,000 ms. Default: 1,000 ms (1 second).
Maximum retriesSet with the MaxReconsumeTimes parameter. No upper limit. Default: Integer.MAX.

Unordered messages

SettingDetails
Retry intervalFollows a predefined escalating schedule (see table below). Custom intervals are not supported for unordered messages.
Maximum retriesSet with the MaxReconsumeTimes parameter. No upper limit. Default: 16.

If MaxReconsumeTimes exceeds 16, the predefined schedule applies for the first 16 retries. All subsequent retries use a fixed 2-hour interval.

Retry interval schedule for unordered messages

RetryIntervalRetryInterval
110 seconds97 minutes
230 seconds108 minutes
31 minute119 minutes
42 minutes1210 minutes
53 minutes1320 minutes
64 minutes1430 minutes
75 minutes151 hour
86 minutes162 hours

Retry policies for HTTP

HTTP retry policies are predefined and cannot be modified.

Message typeRetry intervalMaximum retries
Ordered1 minute288
Unordered5 minutes288

Configure retry behavior for TCP

Enable retry

To trigger a retry when consumption fails in clustering consumption mode, implement the MessageListener interface with one of these approaches:

  • Return Action.ReconsumeLater (recommended)

  • Return null

  • Throw an exception

public class MessageListenerImpl implements MessageListener {

    @Override
    public Action consume(Message message, ConsumeContext context) {
        // If the consumption logic throws an exception, the message is retried.
        doConsumeMessage(message);
        // Method 1: Return Action.ReconsumeLater and retry the message.
        return Action.ReconsumeLater;
        // Method 2: Return null and retry the message.
        return null;
        // Method 3: Throw an exception and retry the message.
        throw new RuntimeException("Consumer Message exception");
    }
}

Disable retry

To prevent redelivery, catch all exceptions in your consumption logic and return Action.CommitMessage:

public class MessageListenerImpl implements MessageListener {

    @Override
    public Action consume(Message message, ConsumeContext context) {
        try {
            doConsumeMessage(message);
        } catch (Throwable e) {
            // Catch all exceptions and return CommitMessage to skip retry.
            return Action.CommitMessage;
        }
        // Consumption succeeded.
        return Action.CommitMessage;
    }
}

Customize the retry interval and maximum retries

Note

Custom retry configuration requires TCP client SDK for Java version 1.2.2 or later. For more information, see Release notes.

Set MaxReconsumeTimes and SuspendTimeMillis in the consumer properties before starting the consumer. Custom retry intervals apply only to ordered messages. Unordered messages always follow the predefined schedule.

Properties properties = new Properties();
// Set the maximum number of retries to 20.
properties.put(PropertyKeyConst.MaxReconsumeTimes, "20");
// Set the retry interval to 3,000 milliseconds (ordered messages only).
properties.put(PropertyKeyConst.SuspendTimeMillis, "3000");
Consumer consumer = ONSFactory.createConsumer(properties);
Important

All consumers in the same consumer group share the retry configuration. The most recently started consumer overwrites the configuration of earlier consumers. Make sure all consumers in a group use identical MaxReconsumeTimes and SuspendTimeMillis values.

Query the retry count

After a consumer receives a message, call message.getReconsumeTimes() to check how many times the message has been retried:

public class MessageListenerImpl implements MessageListener {

    @Override
    public Action consume(Message message, ConsumeContext context) {
        // Get the current retry count.
        System.out.println(message.getReconsumeTimes());
        return Action.CommitMessage;
    }
}

Best practices

  • Start with default retry values. The default schedule for unordered messages (escalating from 10 seconds to 2 hours over 16 retries) works well for most transient failures. Adjust only after observing your system's actual failure patterns.

  • Set up dead-letter queue monitoring. Messages that exhaust all retries are moved to a dead-letter queue. Monitor this queue and set up alerts so you can investigate and reprocess failed messages promptly.

  • Keep consumption logic idempotent. Because messages may be delivered more than once during retries, design your consumer to handle duplicate processing safely.

  • Avoid long-running consumption. If processing takes too long, the message may time out and trigger an unnecessary retry. Break long tasks into smaller steps or increase the consumption timeout.

What's next