Some business scenarios require messages to be delivered at a specific future time rather than immediately. For example, an e-commerce platform needs to cancel unpaid orders after 30 minutes, or a monitoring system needs to trigger periodic health checks. Scheduled messages in ApsaraMQ for RocketMQ let you set a delivery timestamp on each message so the broker holds it and delivers it to consumers at the specified time.
This topic provides sample code for sending and receiving scheduled messages by using the TCP client SDK for Java.
For terminology and constraints, see Scheduled messages and delayed messages.
If you are new to ApsaraMQ for RocketMQ, see the Demo project to set up a working project before implementing scheduled messages.
Use cases
Order timeout handling: Send a scheduled message set to the payment deadline when a user places an order. If the order is still unpaid when the message is delivered, cancel it automatically.
Periodic task triggering: Schedule operations such as daily file cleanup or periodic data synchronization by sending messages with future delivery times.
How scheduled messages work
The producer sets a future delivery timestamp on a message by calling
msg.setStartDeliverTime(timestamp).The broker holds the message until the specified timestamp.
At the scheduled time, the broker delivers the message to subscribed consumers.
Time-setting rules:
| Rule | Behavior |
|---|---|
| Timestamp format | Millisecond-level Unix timestamp. Convert your target delivery time to milliseconds since epoch. |
| Past timestamp | The message is delivered immediately. |
Prerequisites
Before you begin, make sure you have:
The SDK for Java installed. See Prepare the environment
An ApsaraMQ for RocketMQ instance, topic, and consumer group created in the ApsaraMQ for RocketMQ console
An AccessKey pair for your Alibaba Cloud account
(Optional) Logging settings configured
Send a scheduled message
The following example sends a message with a future delivery timestamp. The msg.setStartDeliverTime(timestamp) method tells the broker when to deliver the message.
Replace the placeholders with your actual values:
| Placeholder | Description | Example |
|---|---|---|
<your-topic> | Target topic name | my-scheduled-topic |
<your-tag> | Message tag for consumer filtering | payment-timeout |
<your-message-body> | Message content as a byte array | "Hello MQ".getBytes() |
<your-message-key> | Business-specific unique identifier | ORDERID_100 |
<your-tcp-endpoint> | TCP endpoint from the Instance Details page in the ApsaraMQ for RocketMQ console | -- |
import com.aliyun.openservices.ons.api.Message;
import com.aliyun.openservices.ons.api.ONSFactory;
import com.aliyun.openservices.ons.api.Producer;
import com.aliyun.openservices.ons.api.PropertyKeyConst;
import com.aliyun.openservices.ons.api.SendResult;
import java.text.SimpleDateFormat;
import java.util.Properties;
public class ProducerDelayTest {
public static void main(String[] args) {
Properties properties = new Properties();
// Load AccessKey pair from environment variables
properties.put(PropertyKeyConst.AccessKey, System.getenv("ALIBABA_CLOUD_ACCESS_KEY_ID"));
properties.put(PropertyKeyConst.SecretKey, System.getenv("ALIBABA_CLOUD_ACCESS_KEY_SECRET"));
// TCP endpoint from the Instance Details page in the ApsaraMQ for RocketMQ console
properties.put(PropertyKeyConst.NAMESRV_ADDR, "<your-tcp-endpoint>");
Producer producer = ONSFactory.createProducer(properties);
// Call start() once before sending any messages
producer.start();
Message msg = new Message(
"<your-topic>", // Topic
"<your-tag>", // Tag for consumer-side filtering
"<your-message-body>".getBytes() // Message body in bytes
);
// Optional: set a business key for message tracing in the console
msg.setKey("<your-message-key>");
try {
// Set the delivery timestamp (milliseconds since epoch)
// Example: deliver at 2026-03-15 10:30:00
long deliverTime = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss")
.parse("2026-03-15 10:30:00")
.getTime();
msg.setStartDeliverTime(deliverTime);
SendResult sendResult = producer.send(msg);
System.out.println("Message Id:" + sendResult.getMessageId());
} catch (Exception e) {
// Handle send failure: retry or persist the message
System.out.println("Send failed. Topic: " + msg.getTopic());
e.printStackTrace();
}
// Shut down the producer when the application exits.
// To send messages frequently, keep the producer running instead of
// shutting it down after each send.
producer.shutdown();
}
}Key points:
setStartDeliverTime(long timestamp)accepts a millisecond-level Unix timestamp. The broker holds the message until this time.Call
producer.start()once before sending. For high-throughput scenarios, reuse the same producer instance rather than creating a new one for each message.msg.setKey()is optional but recommended for tracing messages by business ID in the ApsaraMQ for RocketMQ console.
For additional examples, see the ApsaraMQ for RocketMQ code library.
Subscribe to scheduled messages
Subscribe to scheduled messages the same way as normal messages. No special subscription logic is required -- the broker delivers the message to consumers when the scheduled timestamp arrives.
For the subscription code, see Subscribe to messages.
Best practices
Set unique message keys. The message key (
setKey) should be globally unique when possible. Use it to look up messages in the ApsaraMQ for RocketMQ console if delivery issues occur.Reuse the producer instance. Call
producer.shutdown()only when the application exits. Repeatedly creating and destroying producers wastes memory.
Related topics
Scheduled messages and delayed messages -- Scheduling mechanism and constraints
Subscribe to messages -- Set up message consumption
ApsaraMQ for RocketMQ code library -- More Java SDK examples
Demo project -- Complete working project