Zamanlanmış mesajlar, hemen değil, belirtilen bir zamanda tüketicilere teslim edilir. Dağıtılmış uygulamalarda zaman tabanlı tetikleyiciler oluşturmak için kullanın; örneğin, sipariş zaman aşımı iptali, ertelenmiş bildirimler veya periyodik görev planlaması gibi senaryolarda bu özelliği değerlendirebilirsiniz.
Zamanlanmış mesajlar ile gecikmeli mesajlar aynı şekilde çalışır; broker her ikisini de tutar ve belirtilen zaman damgasında teslim eder. Aşağıda "zamanlanmış mesajlar" ifadesi her iki türü de kapsar.
Başlamadan önce
Zamanlanmış mesajlar göndermeden önce şunları doğrulayın:
Konunuzun MessageType özelliği Delay olarak ayarlanmış olmalıdır. Zamanlanmış mesajlar yalnızca Delay türündeki konulara gönderilebilir.
Teslim zaman damgası geçerli saatten sonra olmalıdır. Zaman damgası geçmişteyse veya izin verilen aralığı aşıyorsa broker mesajı hemen teslim eder.
-
Teslim zaman damgası, örnek türünüz için maksimum planlama penceresi içinde kalmalıdır:
| Örnek türü |
Maksimum planlama penceresi |
| Standard Edition (abonelik, kullandıkça öde veya sunucusuz) ve sunucusuz Professional Edition |
7 gün |
| Professional Edition ve Enterprise Platinum Edition (abonelik veya kullandıkça öde) |
40 gün |
Kullanım senaryoları
Dağıtılmış görev planlaması
Geleneksel veritabanı tabanlı zamanlayıcıların yerine farklı zaman çözünürlüklerinde mesajları planlayabilirsiniz. Örneğin, belirli bir saatte günlük dosya temizleme görevini tetikleyebilir veya her 2 dakikada bir hatırlatma bildirimi gönderebilirsiniz. Zamanlanmış mesajlar, dağıtılmış cron işlerini yönetmenin karmaşıklığını ortadan kaldırırken daha yüksek ölçeklenebilirlik sağlar.

Görev zaman aşımı yönetimi
Bir eylemi son tarih geçene kadar erteleyebilirsiniz. Örneğin, e-ticaret senaryolarında ödeme durumunu kontrol etmek için veritabanını sürekli sorgulamak yerine, oluşturulduktan 30 dakika sonra ödenmemiş bir siparişi iptal edebilirsiniz.

Süresi dolmuş kayıtlar için veritabanını sorgulamaya kıyasla zamanlanmış mesajlar şu avantajları sunar:
Esnek zaman çözünürlüğü -- Sabit zaman artışlarına gerek kalmadan ve deduplication mantığı uygulamadan istediğiniz aralıkta görevleri tetikleyin.
Daha yüksek performans -- Sık veritabanı taramalarının yarattığı darboğazlardan kaçının. Mesajlaşma altyapısı eşzamanlılığı yönetir ve yatay olarak ölçeklenir.
Planlamanın çalışma mekanizması
Teslim zaman damgası
Teslim zamanı, milisaniye cinsinden bir Unix zaman damgası olarak ifade edilir. Mesajda setDeliveryTimestamp() yöntemiyle bu değeri ayarlayın. Broker, belirtilen zaman damgasına ulaşılana kadar mesajı bekletir.
Zamanlanmış mesajlar için mutlak teslim zamanını hesaplayın: Geçerli saat 2022-06-09 17:30:00 ise ve teslimi 19:20:00 saatinde yapmak istiyorsanız zaman damgasını 1654773600000 olarak ayarlayın.
Gecikmeli mesajlar için geçerli saate göreli bir ofset ekleyin: Geçerli saat 2022-06-09 17:30:00 ise ve teslimi 1 saat sonra yapmak istiyorsanız zaman damgasını 1654770600000 olarak ayarlayın.
Önemli
Mesaj gönderildikten sonra teslim zaman damgası değiştirilemez veya iptal edilemez.
Mesaj yaşam döngüsü
Zamanlanmış bir mesaj, üretimden silinmeye kadar altı aşamadan geçer:

| Aşama |
Gerçekleşen işlem |
| Başlatma |
Üretici mesajı oluşturur ve broker'a gönderir. |
| Planlandı |
Broker mesajı zaman tabanlı bir depolama sisteminde saklar. Bu aşamada tüketici tarafından görülebilir bir dizin henüz oluşturulmamıştır. |
| Tüketime hazır |
Teslim zaman damgasında mesaj normal depolama motoruna taşınır ve tüketiciler tarafından görülebilir hale gelir. |
| Tüketimde |
Tüketici mesajı alır ve işler. Zaman aşımı içinde onay gelmezse broker teslimi yeniden dener. Daha fazla bilgi için bkz. Consumption retry. |
| Onaylandı |
Tüketici mesajı onaylar. Broker mesajı tüketildi olarak işaretler ancak hemen silmez. |
| Silindi |
Broker, saklama süresi dolduğunda veya depolama alanı azaldığında mesajı yuvarlanarak siler. Silinene kadar mesaj yeniden tüketilebilir. Daha fazla bilgi için bkz. Message storage and cleanup. |
Sınırlamalar
| Kısıtlama |
Açıklama |
| Konu türü |
Yalnızca MessageType özelliği Delay olarak ayarlanmış konular zamanlanmış mesajları kabul eder. |
| Zaman hassasiyeti |
Milisaniye düzeyinde doğruluk sağlar. Varsayılan çözünürlük 1.000 ms'dir. |
| Kalıcılık |
Zamanlanmış mesajlar broker yeniden başlatmalarından etkilenmez. Ancak depolama sistemi hataları veya yeniden başlatmalar kısa süreli teslim gecikmelerine neden olabilir. |
| İptal |
Desteklenmez. Gönderildikten sonra teslim zaman damgası değiştirilemez veya geri alınamaz. |
Zamanlanmış mesajları gönderme ve tüketme
Aşağıdaki örneklerde Apache RocketMQ 5.x Java SDK kullanılmıştır. Her üretici örneğinde setDeliveryTimestamp() ile bir teslim zaman damgası ayarlanır; bu, normal bir mesaj göndermekten tek farkıdır. Tam SDK referansı için bkz. Apache RocketMQ 5.x SDKs.
Bu örnekleri çalıştırmadan önce konunuzun MessageType özelliğinin Delay olarak ayarlandığından emin olun.
Örnek kod
Zamanlanmış mesaj gönderme
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();
}
}
}
Push consumer ile tüketme
Push consumer, mesaj dinleyici geri arama işlevi aracılığıyla mesajları alır. Broker, mesajlar hazır hale geldikçe bunları consumer'a iter.
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();
}
}
Simple consumer ile tüketme
Simple consumer, mesajları broker'dan çeker ve her birini işledikten sonra açıkça onaylar.
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();
}
}
En iyi uygulamalar
Teslim zamanlarını serpiştirin. Çok sayıda mesajı aynı zaman damgasına planlamaktan kaçının. Birçok mesaj aynı teslim zamanını paylaştığında broker hepsini aynı anda işlemek zorunda kalır; bu durum yükü artırır ve teslimatı geciktirebilir. Mümkünse zaman damgalarını bir aralığa yayarak dağıtın.
Tüketicilerde iptal mantığı uygulayın. Mesajı gönderdikten sonra geri alamayacağınız veya yeniden planlayamayacağınız için iptal işlemini tüketici tarafında yönetin. Örneğin, tüketici "ödenmemiş siparişi iptal et" mesajını aldığında önce mevcut sipariş durumunu kontrol edin; kullanıcı zaten ödeme yaptıysa iptal işlemini atlayın.
SSS
Gönderilen bir mesajı iptal edebilir veya yeniden planlayabilir miyim?
Hayır. Mesaj gönderildikten sonra teslim zaman damgası kesindir. Planlanan bir eylemin gereksiz hale geldiği senaryoları yönetmek için bu mantığı tüketiciye entegre edin; örneğin, ödenmemiş bir siparişi iptal etmeden önce mevcut sipariş durumunu kontrol edin.
Geçmiş bir zamana teslim zamanı ayarlarsam ne olur?
Broker mesajı normal bir mesaj gibi hemen teslim eder.
Neden konsolda zamanlanmış mesajımı bulamıyorum?
Zamanlanmış mesajlar, teslim zaman damgasına ulaşılana kadar görünmez kalır. Bu zamandan sonra tekrar kontrol edin; mesaj görünür hale gelir ve tüketiciler tarafından kullanılabilir duruma gelir.