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:
A producer publishes a message with a
delayheader to an exchange.The exchange routes the message to a queue.
Consumers receive the message only after the delay period elapses.

Choose an approach
ApsaraMQ for RabbitMQ supports four delayed message approaches. All open source RabbitMQ approaches work without code changes.
| Approach | Open source RabbitMQ | ApsaraMQ for RabbitMQ | Tradeoffs |
|---|---|---|---|
| Native delayed message | Not supported | Supported | Simplest setup. Set a single header; no extra exchanges or queues needed. No head-of-line blocking. |
| Open source delayed message plug-in | Supported (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 TTL | Supported | Supported | All 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 TTL | Supported | Supported | Per-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 periodFor 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 keydelaywith a string value (for example,"5000"), while the open source plug-in approach usesx-delaywith 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:
| Placeholder | Description | Example |
|---|---|---|
<your-instance-endpoint> | ApsaraMQ for RabbitMQ instance endpoint | xxxxx.mq-amqp.cn-hangzhou-a.aliyuncs.com |
<your-access-key> | Instance username (AccessKey-based) | MjoxODgwNTkxODk0NTg3**** |
<your-secret-key> | Instance password | OTRkYjNlNjMwZjM1Nzk4OWFmYTk5MTQ1**** |
<exchange-name> | Target exchange name | my-exchange |
<routing-key> | Routing key for the message | order.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);| Parameter | Description |
|---|---|
ExchangeName | The exchange name. The exchange must already exist in the ApsaraMQ for RabbitMQ console. |
x-delayed-message | The exchange type for delayed message routing. |
x-delayed-type | The 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
Message TTL -- Configure expiration for messages.
Cluster limits -- Check the maximum delay period for your instance type.
Create an exchange -- Set up exchanges in the ApsaraMQ for RabbitMQ console.
AMQP Demos -- Sample code in multiple programming languages.