All Products
Search
Document Center

ApsaraMQ for RocketMQ:Scheduled and delayed messages

Last Updated:Mar 11, 2026

Scheduled messages are delivered to consumers at a specified time instead of immediately. Use them to build time-based triggers in distributed applications -- such as order timeout cancellation, deferred notifications, or periodic task scheduling.

Scheduled messages and delayed messages work the same way -- the broker holds both and delivers them at a specified timestamp. The term "scheduled messages" below covers both types.

Before you begin

Before you send scheduled messages, make sure that:

  • Your topic has its MessageType set to Delay. Scheduled messages can only be sent to Delay-type topics.

  • The delivery timestamp is later than the current time. If the timestamp is in the past or exceeds the allowed range, the broker delivers the message immediately.

  • The delivery timestamp falls within the maximum scheduling window for your instance type:

    Instance typeMaximum scheduling window
    Standard Edition (subscription, pay-as-you-go, or serverless) and serverless Professional Edition7 days
    Professional Edition and Enterprise Platinum Edition (subscription or pay-as-you-go)40 days

Use cases

Distributed task scheduling

Schedule messages at varying time granularities to replace traditional database-based timers. For example, trigger a file cleanup task daily at a fixed time, or send a reminder notification every 2 minutes. Scheduled messages eliminate the complexity of managing distributed cron jobs while providing higher scalability.

Distributed timed scheduling

Task timeout handling

Delay an action until a deadline passes. For example, in e-commerce, cancel an unpaid order 30 minutes after creation instead of polling a database to check payment status.

Task timeout processing

Compared to polling a database for expired records, scheduled messages offer:

  • Flexible time granularity -- Trigger tasks at any interval, with no fixed time increments and no need for deduplication logic.

  • Higher performance -- Avoid the bottlenecks of frequent database scans. The messaging infrastructure handles concurrency and scales horizontally.

How scheduling works

Delivery timestamp

The delivery time is expressed as a Unix timestamp in milliseconds. Set it on the message with setDeliveryTimestamp(). The broker holds the message until that timestamp is reached.

  • For scheduled messages, calculate the absolute delivery time: If the current time is 2022-06-09 17:30:00 and you want delivery at 19:20:00, set the timestamp to 1654773600000.

  • For delayed messages, add a relative offset to the current time: If the current time is 2022-06-09 17:30:00 and you want delivery after 1 hour, set the timestamp to 1654770600000.

Important

The delivery timestamp cannot be changed or canceled after the message is sent.

Message lifecycle

A scheduled message goes through six stages from production to deletion:

Scheduled message lifecycle
StageWhat happens
InitializationThe producer builds the message and sends it to the broker.
ScheduledThe broker stores the message in a time-based storage system. No consumer-visible index is created yet.
Ready for consumptionAt the delivery timestamp, the message moves to the regular storage engine and becomes visible to consumers.
In consumptionA consumer receives and processes the message. If no acknowledgment arrives within the timeout, the broker retries delivery. For more information, see Consumption retry.
CommittedThe consumer acknowledges the message. The broker marks it as consumed but does not delete it immediately.
DeletedThe broker deletes the message in a rolling manner when the retention period expires or storage space runs low. Until deletion, the message can be re-consumed. For more information, see Message storage and cleanup.

Limits

ConstraintDetail
Topic typeOnly topics with MessageType set to Delay accept scheduled messages.
Time precisionAccurate to milliseconds. Default granularity is 1,000 ms.
PersistenceScheduled messages survive broker restarts. However, storage system exceptions or restarts may cause brief delivery delays.
CancellationNot supported. Once sent, the delivery timestamp cannot be changed or revoked.

Send and consume scheduled messages

The following examples use the Apache RocketMQ 5.x Java SDK. Each producer example sets a delivery timestamp with setDeliveryTimestamp() -- this is the only difference from sending a normal message. For the complete SDK reference, see Apache RocketMQ 5.x SDKs.

Make sure your topic is created with MessageType set to Delay before running these examples.

Sample code

Send a scheduled message

import org.apache.rocketmq.client.apis.*;
import org.apache.rocketmq.client.apis.message.Message;
import org.apache.rocketmq.client.apis.producer.Producer;
import org.apache.rocketmq.client.apis.producer.SendReceipt;


public class ProducerExample {
    public static void main(String[] args) throws ClientException {
        // Instance endpoint. Find this on the Endpoints tab of the Instance Details
        // page in the ApsaraMQ for RocketMQ console.
        // For internal access from ECS in the same VPC, use the VPC endpoint.
        // For public access, use the public endpoint and enable Internet access
        // on the instance.
        String endpoints = "rmq-cn-xxx.{regionId}.rmq.aliyuncs.com:8080";

        // Topic name. Create this topic in the console first, with MessageType
        // set to Delay.
        String topic = "Your Topic";

        ClientServiceProvider provider = ClientServiceProvider.loadService();
        ClientConfigurationBuilder builder = ClientConfiguration.newBuilder().setEndpoints(endpoints);

        // For public endpoint access, provide your instance username and password.
        // Find these on the Intelligent Authentication tab of the Access Control
        // page in the console.
        // For VPC access from ECS, credentials are obtained automatically.
        // For serverless instances with authentication-free in VPCs enabled,
        // credentials are not required for VPC access.
        //builder.setCredentialProvider(new StaticSessionCredentialsProvider("Instance UserName", "Instance Password"));

        ClientConfiguration configuration = builder.build();
        Producer producer = provider.newProducerBuilder()
                .setTopics(topic)
                .setClientConfiguration(configuration)
                .build();

        // Deliver the message 10 minutes from now
        long deliverTimeStamp = System.currentTimeMillis() + 10L * 60 * 1000;

        Message message = provider.newMessageBuilder()
                .setTopic("topic")
                .setKeys("messageKey")       // Message key for tracing
                .setTag("messageTag")        // Tag for consumer-side filtering
                .setDeliveryTimestamp(deliverTimeStamp)
                .setBody("messageBody".getBytes())
                .build();
        try {
            SendReceipt sendReceipt = producer.send(message);
            System.out.println(sendReceipt.getMessageId());
        } catch (ClientException e) {
            e.printStackTrace();
        }
    }
}

Consume with a push consumer

A push consumer receives messages through a message listener callback. The broker pushes messages to the consumer as they become ready.

import org.apache.rocketmq.client.apis.*;
import org.apache.rocketmq.client.apis.consumer.ConsumeResult;
import org.apache.rocketmq.client.apis.consumer.FilterExpression;
import org.apache.rocketmq.client.apis.consumer.FilterExpressionType;
import org.apache.rocketmq.client.apis.consumer.PushConsumer;
import org.apache.rocketmq.shaded.org.slf4j.Logger;
import org.apache.rocketmq.shaded.org.slf4j.LoggerFactory;

import java.io.IOException;
import java.util.Collections;

public class PushConsumerExample {
    private static final Logger LOGGER = LoggerFactory.getLogger(PushConsumerExample.class);

    private PushConsumerExample() {
    }

    public static void main(String[] args) throws ClientException, IOException, InterruptedException {
        // Instance endpoint (see producer example for details)
        String endpoints = "rmq-cn-xxx.{regionId}.rmq.aliyuncs.com:8080";

        // Create the topic and consumer group in the console before running
        // this example
        String topic = "Your Topic";
        String consumerGroup = "Your ConsumerGroup";

        final ClientServiceProvider provider = ClientServiceProvider.loadService();
        ClientConfigurationBuilder builder = ClientConfiguration.newBuilder().setEndpoints(endpoints);

        // Uncomment for public endpoint access (see producer example for details)
        //builder.setCredentialProvider(new StaticSessionCredentialsProvider("Instance UserName", "Instance Password"));
        ClientConfiguration clientConfiguration = builder.build();

        // Subscribe to all messages in the topic (tag = "*")
        String tag = "*";
        FilterExpression filterExpression = new FilterExpression(tag, FilterExpressionType.TAG);

        PushConsumer pushConsumer = provider.newPushConsumerBuilder()
                .setClientConfiguration(clientConfiguration)
                .setConsumerGroup(consumerGroup)
                .setSubscriptionExpressions(Collections.singletonMap(topic, filterExpression))
                .setMessageListener(messageView -> {
                    System.out.println("Consume Message: " + messageView);
                    return ConsumeResult.SUCCESS;
                })
                .build();

        Thread.sleep(Long.MAX_VALUE);
        // Shut down when no longer needed
        //pushConsumer.close();
    }
}

Consume with a simple consumer

A simple consumer pulls messages from the broker and explicitly acknowledges each one after processing.

import org.apache.rocketmq.client.apis.*;
import org.apache.rocketmq.client.apis.consumer.FilterExpression;
import org.apache.rocketmq.client.apis.consumer.FilterExpressionType;
import org.apache.rocketmq.client.apis.consumer.SimpleConsumer;
import org.apache.rocketmq.client.apis.message.MessageId;
import org.apache.rocketmq.client.apis.message.MessageView;
import org.apache.rocketmq.shaded.org.slf4j.Logger;
import org.apache.rocketmq.shaded.org.slf4j.LoggerFactory;

import java.io.IOException;
import java.time.Duration;
import java.util.Collections;
import java.util.List;

public class SimpleConsumerExample {
    private static final Logger LOGGER = LoggerFactory.getLogger(SimpleConsumerExample.class);

    private SimpleConsumerExample() {
    }

    public static void main(String[] args) throws ClientException, IOException {
        // Instance endpoint (see producer example for details)
        String endpoints = "rmq-cn-xxx.{regionId}.rmq.aliyuncs.com:8080";

        // Create the topic and consumer group in the console before running
        // this example
        String topic = "Your Topic";
        String consumerGroup = "Your ConsumerGroup";

        final ClientServiceProvider provider = ClientServiceProvider.loadService();
        ClientConfigurationBuilder builder = ClientConfiguration.newBuilder().setEndpoints(endpoints);

        // Uncomment for public endpoint access (see producer example for details)
        //builder.setCredentialProvider(new StaticSessionCredentialsProvider("Instance UserName", "Instance Password"));
        ClientConfiguration clientConfiguration = builder.build();

        // Long-polling timeout
        Duration awaitDuration = Duration.ofSeconds(10);

        // Subscribe to all messages in the topic (tag = "*")
        String tag = "*";
        FilterExpression filterExpression = new FilterExpression(tag, FilterExpressionType.TAG);

        SimpleConsumer consumer = provider.newSimpleConsumerBuilder()
                .setClientConfiguration(clientConfiguration)
                .setConsumerGroup(consumerGroup)
                .setAwaitDuration(awaitDuration)
                .setSubscriptionExpressions(Collections.singletonMap(topic, filterExpression))
                .build();

        int maxMessageNum = 16;
        Duration invisibleDuration = Duration.ofSeconds(10);

        // Pull and process messages in a loop.
        // For real-time consumption, use multiple threads to pull concurrently.
        while (true) {
            final List<MessageView> messages = consumer.receive(maxMessageNum, invisibleDuration);
            messages.forEach(messageView -> {
                System.out.println("Received message: " + messageView);
            });
            for (MessageView message : messages) {
                final MessageId messageId = message.getMessageId();
                try {
                    consumer.ack(message);
                    System.out.println("Message is acknowledged successfully, messageId= " + messageId);
                } catch (Throwable t) {
                    t.printStackTrace();
                }
            }
        }
        // Shut down when no longer needed
        // consumer.close();
    }
}

Best practices

Stagger delivery times. Avoid scheduling a large batch of messages for the exact same timestamp. When many messages share a delivery time, the broker must process them all simultaneously, which increases load and can delay delivery. Spread timestamps across a range when possible.

Build cancellation logic in consumers. Because you cannot revoke or reschedule a message after sending it, handle cancellation at the consumer side. For example, when a consumer receives a "cancel unpaid order" message, check the current order status first -- if the user has already paid, skip the cancellation.

FAQ

Can I cancel or reschedule a message after sending it?

No. The delivery timestamp is final once the message is sent. To handle scenarios where a scheduled action becomes unnecessary, build that logic into your consumer -- for example, check the current order status before canceling an unpaid order.

What happens if I set a delivery time in the past?

The broker delivers it immediately, just like a normal message.

Why can't I find my scheduled message in the console?

Scheduled messages stay invisible until the delivery timestamp is reached. Check back after that time -- the message appears and becomes available to consumers.