Aşağıdaki PHP kod örnekleri, ApsaraMQ for RocketMQ HTTP istemci SDK'sı ile zamanlanmış ve gecikmeli mesajların nasıl gönderileceğini ve tüketileceğini gösterir.
Zamanlanmış ve gecikmeli mesajların çalışma mantığı
Zamanlanmış ve gecikmeli mesajlar, yayınlandıktan hemen sonra ulaşmak yerine teslimatı erteler.
Gecikmeli mesaj: Belirtilen bir gecikme süresinin ardından teslim edilir. Örnek: Gönderildikten 30 saniye sonra teslim edin.
Zamanlanmış mesaj: Belirli bir zamanda teslim edilir. Örnek: 2024-06-15 09:00:00 tarihinde teslim edin.
Her iki tür de aynı API'yi kullanır. Aracının (broker) mesajı ne zaman teslim etmesi gerektiğini temsil eden milisaniye düzeyinde bir Unix zaman damgasıyla setStartDeliverTime yöntemini çağırın.
Bir gecikmeli mesaj için zaman damgasını
geçerli saat + gecikme süresiolarak hesaplayın.Bir zamanlanmış mesaj için hedef teslim saatini doğrudan milisaniye düzeyinde bir Unix zaman damgasına dönüştürün.
Yaygın kullanım senaryoları:
Sipariş zaman aşımı: Sipariş oluşturulduğunda gecikmeli bir mesaj yayınlayarak ödenmemiş bir siparişi 30 dakika sonra iptal edin.
Geri çekilmeli yeniden deneme: Başarısız olan bir görevi hemen yeniden denemek yerine bir gecikmeyle tekrar işleyin.
Zamanlı bildirimler: Planlanmış bir etkinlikten 15 dakika önce gibi belirli bir zamanda hatırlatıcı tetikleyin.
Daha fazla bilgi için bkz. Zamanlanmış mesajlar ve gecikmeli mesajlar.
Ön koşullar
Başlamadan önce aşağıdakileri yaptığınızdan emin olun:
ApsaraMQ for RocketMQ konsolunda bir örnek, bir konu ve bir tüketici grubu dahil olmak üzere kaynakları oluşturun
Bir AccessKey çifti oluşturun ve AccessKey ID ile AccessKey secret değerlerini ortam değişkenleri olarak kaydedin
Zamanlanmış veya gecikmeli mesajlar gönderme
Aşağıdaki kod, her biri yayınlamadan 10 saniye sonra teslim edilen dört gecikmeli mesaj gönderir. Bunun yerine zamanlanmış bir mesaj göndermek için hedef teslim saatini milisaniye düzeyinde bir Unix zaman damgası olarak setStartDeliverTime yöntemine iletin.
Kodu çalıştırmadan önce yer tutucuları değiştirin:
| Yer Tutucu | Açıklama | Örnek |
|---|---|---|
<HTTP_ENDPOINT> |
ApsaraMQ for RocketMQ konsolundaki Instance Details sayfasından alınan HTTP uç noktası | http://xxxx.mq-http.cn-hangzhou.aliyuncs.com |
<TOPIC> |
Konsolda oluşturulan konu adı | scheduled-msg-topic |
<INSTANCE_ID> |
Örnek kimliği. Örneğin ad alanı yoksa null veya "" olarak ayarlayın |
MQ_INST_xxxx |
<?php
require "vendor/autoload.php";
use MQ\Model\TopicMessage;
use MQ\MQClient;
class ProducerTest
{
private $client;
private $producer;
public function __construct()
{
$this->client = new MQClient(
// HTTP endpoint from the Instance Details page in the ApsaraMQ for RocketMQ console
"<HTTP_ENDPOINT>",
// AccessKey ID for authentication
getenv("ALIBABA_CLOUD_ACCESS_KEY_ID"),
// AccessKey secret for authentication
getenv('ALIBABA_CLOUD_ACCESS_KEY_SECRET')
);
$topic = "<TOPIC>";
// Instance ID. If the instance has no namespace, set to null or "".
$instanceId = "<INSTANCE_ID>";
$this->producer = $this->client->getProducer($instanceId, $topic);
}
public function run()
{
try
{
for ($i = 1; $i <= 4; $i++)
{
$publishMessage = new TopicMessage(
"hello mq! " // Message body
);
// Custom message property
$publishMessage->putProperty("a", $i);
// Message key for tracing
$publishMessage->setMessageKey("MessageKey");
// Deliver the message 10 seconds from now.
// For a scheduled message, set this to the target delivery time
// as a millisecond-level Unix timestamp.
$publishMessage->setStartDeliverTime(time() * 1000 + 10 * 1000);
$result = $this->producer->publishMessage($publishMessage);
print "Send mq message success. msgId is:" . $result->getMessageId()
. ", bodyMD5 is:" . $result->getMessageBodyMD5() . "\n";
}
} catch (\Exception $e) {
print_r($e->getMessage() . "\n");
}
}
}
$instance = new ProducerTest();
$instance->run();
?>
Önemli noktalar:
setStartDeliverTimeyöntemi milisaniye düzeyinde bir Unix zaman damgası kabul eder. PHP'detime()saniye döndürür, bu nedenle dönüştürmek için 1000 ile çarpın.10 saniyelik bir gecikme için:
time() * 1000 + 10 * 1000.Belirli bir zamanda teslim etmek için o saati milisaniye cinsinden bir Unix zaman damgasına dönüştürün. Örneğin,
2024-06-15 09:00:00 UTCtarihinde teslim etmek için:strtotime('2024-06-15 09:00:00') * 1000.
Zamanlanmış veya gecikmeli mesajları tüketme
Zamanlanmış ve gecikmeli mesajlar, normal mesajlarla aynı şekilde tüketilir. Teslimat zamanı geldiğinde mesajlar uzun polling yoluyla kullanılabilir hale gelir.
Kodu çalıştırmadan önce yer tutucuları değiştirin:
| Yer Tutucu | Açıklama | Örnek |
|---|---|---|
<HTTP_ENDPOINT> |
Instance Details sayfasından alınan HTTP uç noktası | http://xxxx.mq-http.cn-hangzhou.aliyuncs.com |
<TOPIC> |
Konsolda oluşturulan konu adı | scheduled-msg-topic |
<GROUP_ID> |
Konsolda oluşturulan tüketici grubu kimliği | GID_scheduled_consumer |
<INSTANCE_ID> |
Örnek kimliği. Örneğin ad alanı yoksa null veya "" olarak ayarlayın |
MQ_INST_xxxx |
<?php
use MQ\MQClient;
require "vendor/autoload.php";
class ConsumerTest
{
private $client;
private $consumer;
public function __construct()
{
$this->client = new MQClient(
// HTTP endpoint from the Instance Details page in the ApsaraMQ for RocketMQ console
"<HTTP_ENDPOINT>",
// AccessKey ID for authentication
getenv("ALIBABA_CLOUD_ACCESS_KEY_ID"),
// AccessKey secret for authentication
getenv('ALIBABA_CLOUD_ACCESS_KEY_SECRET')
);
$topic = "<TOPIC>";
// Consumer group ID created in the ApsaraMQ for RocketMQ console
$groupId = "<GROUP_ID>";
// Instance ID. If the instance has no namespace, set to null or "".
$instanceId = "<INSTANCE_ID>";
$this->consumer = $this->client->getConsumer($instanceId, $topic, $groupId);
}
public function ackMessages($receiptHandles)
{
try {
$this->consumer->ackMessage($receiptHandles);
} catch (\Exception $e) {
if ($e instanceof MQ\Exception\AckMessageException) {
// ACK may fail if the receipt handle has expired
printf("Ack Error, RequestId:%s\n", $e->getRequestId());
foreach ($e->getAckMessageErrorItems() as $errorItem) {
printf("\tReceiptHandle:%s, ErrorCode:%s, ErrorMsg:%s\n",
$errorItem->getReceiptHandle(),
$errorItem->getErrorCode(),
$errorItem->getErrorMessage());
}
}
}
}
public function run()
{
// Consume messages in a loop. For production use, run multiple threads
// to consume messages concurrently.
while (True) {
try {
// Long polling: if no message is available, the request waits
// on the broker until a message arrives or the polling period ends.
$messages = $this->consumer->consumeMessage(
3, // Max messages per request (up to 16)
3 // Long polling timeout in seconds (up to 30)
);
} catch (\MQ\Exception\MessageResolveException $e) {
// Thrown when some messages contain invalid characters and cannot be parsed
$messages = $e->getPartialResult()->getMessages();
$failMessages = $e->getPartialResult()->getFailResolveMessages();
$receiptHandles = array();
foreach ($messages as $message) {
$receiptHandles[] = $message->getReceiptHandle();
printf("MsgID %s\n", $message->getMessageId());
}
foreach ($failMessages as $failMessage) {
$receiptHandles[] = $failMessage->getReceiptHandle();
printf("Fail To Resolve Message. MsgID %s\n", $failMessage->getMessageId());
}
$this->ackMessages($receiptHandles);
continue;
} catch (\Exception $e) {
if ($e instanceof MQ\Exception\MessageNotExistException) {
// No messages available. Long polling continues on the next iteration.
printf("No message, continue long polling! RequestId:%s\n", $e->getRequestId());
continue;
}
print_r($e->getMessage() . "\n");
sleep(3);
continue;
}
print "consume finish, messages:\n";
$receiptHandles = array();
foreach ($messages as $message) {
$receiptHandles[] = $message->getReceiptHandle();
printf("MessageID:%s TAG:%s BODY:%s \nPublishTime:%d, FirstConsumeTime:%d, \nConsumedTimes:%d, NextConsumeTime:%d, MessageKey:%s\n",
$message->getMessageId(), $message->getMessageTag(), $message->getMessageBody(),
$message->getPublishTime(), $message->getFirstConsumeTime(),
$message->getConsumedTimes(), $message->getNextConsumeTime(),
$message->getMessageKey());
print_r($message->getProperties());
}
// If the broker does not receive an ACK before the time specified
// in getNextConsumeTime(), the message is redelivered.
// Each delivery generates a new receipt handle.
print_r($receiptHandles);
$this->ackMessages($receiptHandles);
print "ack finish\n";
}
}
}
$instance = new ConsumerTest();
$instance->run();
?>
Mesaj tüketimi hakkında bilinmesi gerekenler
Uzun polling: Kullanılabilir bir mesaj olmadığında, aracı (broker) bir mesaj gelene kadar veya polling zaman aşımı dolana kadar tüketicinin isteğini bekletir. Bu, gereksiz ağ isteklerini azaltır.
ACK (tüketim onayı): Tüketici bir mesajı işledikten sonra makbuz tanıtıcısı (receipt handle) ile birlikte bir ACK gönderir. Aracı,
getNextConsumeTime()öncesinde ACK almazsa mesajı yeniden teslim eder.Makbuz tanıtıcısının sona ermesi: Her teslimat, kendi sona erme süresine sahip benzersiz bir makbuz tanıtıcısı atar. Süresi dolmuş bir tanıtıcı, ACK işleminin başarısız olmasına ve yeniden teslimata neden olur.
Ayrıştırma hataları: Bir mesaj gövdesi geçersiz karakterler içeriyorsa SDK
MessageResolveExceptionözel durumu oluşturur. Başarıyla ayrıştırılan mesajları almak ve hataları ayrı olarak işlemek içingetPartialResult()yöntemini çağırın.