All Products
Search
Document Center

ApsaraMQ for RabbitMQ:Delayed messages

Last Updated:Mar 11, 2026

A delayed message is delivered to consumers only after a specified delay period. ApsaraMQ for RabbitMQ supports delayed messages natively and is fully compatible with open source RabbitMQ delayed message approaches -- no code changes required to migrate.

Use cases

  • Order timeout handling: After an order is created, send a delayed message with a 30-minute delay. When the consumer receives the message, it checks whether payment is complete. If not, the order is closed.

  • Scheduled notifications: Trigger tasks at a future time, such as sending a reminder 24 hours before an appointment.

  • Retry with backoff: Requeue failed messages with progressively longer delays (for example, 1s, 2s, 4s, 8s) to avoid overwhelming downstream services.

How it works

The native delayed message solution requires no plug-in installation or dead-letter exchange topology:

  1. A producer publishes a message with a delay header to an exchange.

  2. The exchange routes the message to a queue.

  3. Consumers receive the message only after the delay period elapses.

Delayed message flow

Choose an approach

ApsaraMQ for RabbitMQ supports four delayed message approaches. All open source RabbitMQ approaches work without code changes.

ApproachOpen source RabbitMQApsaraMQ for RabbitMQTradeoffs
Native delayed messageNot supportedSupportedSimplest setup. Set a single header; no extra exchanges or queues needed. No head-of-line blocking.
Open source delayed message plug-inSupported (requires plug-in installation)Supported (no installation needed)Compatible with existing open source code. Requires declaring a special exchange type (x-delayed-message).
Dead-letter exchange + queue-level TTLSupportedSupportedAll messages in a queue share the same delay. Subject to head-of-line blocking: messages must expire in FIFO order.
Dead-letter exchange + message-level TTLSupportedSupportedPer-message delays, but still subject to head-of-line blocking within a queue.
Head-of-line blocking means messages in a queue must expire in order. A message with a shorter TTL cannot expire before an earlier message with a longer TTL, which can cause unexpected delays.

For new implementations, use the native delayed message approach -- it has the simplest setup and avoids head-of-line blocking.

Usage notes

Delay period

  • The delay value must be a non-negative integer in milliseconds.

  • If the delay exceeds the maximum allowed for your instance type, the message is delivered immediately as a normal message. For maximum delay values, see Cluster limits.

Interaction with TTL

When time-to-live (TTL) is configured for delayed messages, the actual TTL is calculated as:

Actual message TTL = min(message-level TTL, queue-level TTL) + delay period

For more information, see Message TTL.

Consumer mode

Use basic.consume (push mode) instead of basic.get (pull mode). ApsaraMQ for RabbitMQ stores messages across distributed nodes. In pull mode, the consumer may not reach the node that holds the delayed message, which causes delivery later than expected.

Send delayed messages (native approach)

Add a delay key to the message headers with the delay value in milliseconds as a string.

The native approach uses the header key delay with a string value (for example, "5000"), while the open source plug-in approach uses x-delay with an integer value (for example, 5000).
import com.rabbitmq.client.AMQP;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;

import java.util.HashMap;
import java.util.Map;
import java.util.UUID;

// Establish a connection to the ApsaraMQ for RabbitMQ instance
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("<your-instance-endpoint>");
factory.setPort(5672);
factory.setUsername("<your-access-key>");
factory.setPassword("<your-secret-key>");

Connection connection = factory.newConnection();
Channel channel = connection.createChannel();

// Set the delay to 5000 milliseconds (5 seconds)
Map<String, Object> headers = new HashMap<>();
headers.put("delay", "5000");

AMQP.BasicProperties props = new AMQP.BasicProperties.Builder()
    .messageId(UUID.randomUUID().toString())
    .headers(headers)
    .build();

// Publish the delayed message to the target exchange
channel.basicPublish("<exchange-name>", "<routing-key>", props, "order timeout check".getBytes("UTF-8"));

Replace the following placeholders with actual values:

PlaceholderDescriptionExample
<your-instance-endpoint>ApsaraMQ for RabbitMQ instance endpointxxxxx.mq-amqp.cn-hangzhou-a.aliyuncs.com
<your-access-key>Instance username (AccessKey-based)MjoxODgwNTkxODk0NTg3****
<your-secret-key>Instance passwordOTRkYjNlNjMwZjM1Nzk4OWFmYTk5MTQ1****
<exchange-name>Target exchange namemy-exchange
<routing-key>Routing key for the messageorder.timeout

For sample code in other languages, see AMQP Demos.

Send delayed messages (open source plug-in approach)

ApsaraMQ for RabbitMQ is compatible with the open source delayed message exchange plug-in. No plug-in installation is required.

Step 1: Declare a delayed message exchange

Declare an exchange of type x-delayed-message and set the x-delayed-type argument to specify the routing behavior.

Map<String, Object> args = new HashMap<String, Object>();
args.put("x-delayed-type", "direct");
channel.exchangeDeclare("ExchangeName", "x-delayed-message", true, false, args);
ParameterDescription
ExchangeNameThe exchange name. The exchange must already exist in the ApsaraMQ for RabbitMQ console.
x-delayed-messageThe exchange type for delayed message routing.
x-delayed-typeThe underlying routing algorithm. Valid values: direct, fanout, topic, headers, x-jms-topic.

Step 2: Publish a delayed message

Set the x-delay header to the delay value in milliseconds.

byte[] messageBodyBytes = "delayed payload".getBytes("UTF-8");

Map<String, Object> headers = new HashMap<String, Object>();
headers.put("x-delay", 5000);  // Delay for 5000 milliseconds (5 seconds)

AMQP.BasicProperties.Builder props = new AMQP.BasicProperties.Builder().headers(headers);
channel.basicPublish("ExchangeName", "", props.build(), messageBodyBytes);

FAQ

Why is my message delivered later than the specified delay?

This typically happens when the consumer uses basic.get (pull mode). ApsaraMQ for RabbitMQ stores messages across a cluster of distributed nodes. In pull mode, each basic.get call reaches only one node, so the consumer may not immediately pull the message from the node where it is stored. Switch to basic.consume (push mode) to receive messages as soon as the delay period ends.

What's next