ApsaraMQ for RocketMQ, sıralı mesajları kesin ilk giren ilk çıkar (FIFO) düzeninde iletir. Bu konuda, HTTP istemci SDK'si aracılığıyla sıralı mesajların nasıl gönderileceği ve alınacağına ilişkin PHP örnek kodları sunulmaktadır.
Sıralama türleri
ApsaraMQ for RocketMQ iki sıralama kapsamını destekler:
| Tür | Sıralama kapsamı | Çalışma şekli |
|---|---|---|
| Genel olarak sıralı | Tüm konu | Konudaki tüm mesajlar FIFO düzeninde gönderilir ve tüketilir |
| Bölümlere göre sıralı | Bölüm başına | Mesajlar parçalama anahtarına göre bölümlere dağıtılır. Her bölümdeki mesajlar FIFO düzeninde tüketilir |
Bir parçalama anahtarı, bir mesajın hangi bölüme ait olduğunu belirler. Bu kavram, mesaj anahtarından farklıdır.
Daha fazla bilgi için bkz. Ordered messages.
Sıralama garantileri
Sıralı iletim, hem üreticinin hem de tüketicinin doğru davranmasına bağlıdır.
Aracı, tek bir üretici veya iş parçacığının mesajları gönderdiği sıraya göre mesaj sırasını belirler. Birden çok üretici veya iş parçacığı eş zamanlı olarak mesaj gönderiyorsa sıra, mesajların aracıya varış sırasına göre belirlenir; bu durum, amaçlanan iş sırasından farklı olabilir.
Tüketici tarafı:
Tüketici, tek bir toplu işlemde birden çok bölümden sıralı mesajları çekebilir. Her bölümden gelen mesajlar kendi gönderim sırasını korur.
Tüketici, o bölümden sonraki toplu işlemi almadan önce bir bölümden gelen tüm mesajları onaylamalıdır (ACK).
Aracı,
getNextConsumeTime()tarafından belirtilen zaman aşımından önce bir ACK almazsa mesajı yeniden iletir.
Ön koşullar
Başlamadan önce aşağıdaki gereksinimleri karşıladığınızdan emin olun:
ApsaraMQ for RocketMQ için PHP SDK yüklendi. Ayrıntılar için bkz. Prepare the environment
ApsaraMQ for RocketMQ console içinde bir ApsaraMQ for RocketMQ örneği, konusu ve tüketici grubu oluşturun.
Alibaba Cloud hesabınız için bir AccessKey çifti oluşturun. Ayrıntılar için bkz. Create an AccessKey pair
Sıralı mesaj gönderme
Aşağıdaki kod, dört sıralı mesaj gönderir ve bunları setShardingKey() kullanarak iki bölüme dağıtır. Aynı parçalama anahtarını paylaşan mesajlar, ilgili bölüm içinde FIFO düzeninde iletilir.
<?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. Find this in the HTTP Endpoint section on the Instance Details
// page in the ApsaraMQ for RocketMQ console.
"${HTTP_ENDPOINT}",
// Get AccessKey credentials from environment variables.
getenv("ALIBABA_CLOUD_ACCESS_KEY_ID"),
getenv('ALIBABA_CLOUD_ACCESS_KEY_SECRET')
);
// Topic created in the ApsaraMQ for RocketMQ console.
$topic = "${TOPIC}";
// Instance ID. If the instance has no namespace, set this to null or "".
// Check the Instance Details page for namespace information.
$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
);
// Set a custom message property.
$publishMessage->putProperty("a", $i);
// Set the sharding key to distribute messages across partitions.
// Messages with the same sharding key are delivered in FIFO order.
$publishMessage->setShardingKey($i % 2);
$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();
?>
Aşağıdaki yer tutucuları gerçek değerlerinizle değiştirin:
| Yer Tutucu | Açıklama |
|---|---|
${HTTP_ENDPOINT} |
Örnek Ayrıntıları sayfasındaki HTTP uç noktası |
${TOPIC} |
Konu adı |
${INSTANCE_ID} |
Örnek kimliği (ad alanı yoksa null veya "") |
Sıralı mesaj alma
Aşağıdaki kod, her bölümde FIFO düzenini koruyan consumeMessageOrderly() yöntemini kullanarak sıralı mesajları tüketir. Tüketici uzun yoklama kullanır: Mesaj yoksa istek, bir mesaj gelene veya yoklama zaman aşımı dolana kadar aracıda bekletilir.
<?php
require "vendor/autoload.php";
use MQ\MQClient;
class ConsumerTest
{
private $client;
private $consumer;
public function __construct()
{
$this->client = new MQClient(
// HTTP endpoint. Find this in the HTTP Endpoint section on the Instance Details
// page in the ApsaraMQ for RocketMQ console.
"${HTTP_ENDPOINT}",
// Get AccessKey credentials from environment variables.
getenv("ALIBABA_CLOUD_ACCESS_KEY_ID"),
getenv('ALIBABA_CLOUD_ACCESS_KEY_SECRET')
);
// Topic created in the ApsaraMQ for RocketMQ console.
$topic = "${TOPIC}";
// Consumer group ID created in the ApsaraMQ for RocketMQ console.
$groupId = "${GROUP_ID}";
// Instance ID. If the instance has no namespace, set this to null or "".
// Check the Instance Details page for namespace information.
$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) {
// Handle ACK failures. This occurs when a 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->getErrorCode());
}
}
}
}
public function run()
{
// Poll for messages continuously. For production use, run multiple threads
// to consume messages concurrently across partitions.
while (True) {
try {
// consumeMessageOrderly() ensures FIFO order within each partition.
// If the broker does not receive an ACK for a message, it redelivers
// that message before sending subsequent messages from the same partition.
$messages = $this->consumer->consumeMessageOrderly(
3, // Max messages per batch (up to 16)
3 // Long polling timeout in seconds (up to 30)
);
} catch (\MQ\Exception\MessageResolveException $e) {
// Handle messages that cannot be parsed due to invalid characters.
$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 automatically.
printf("No message, contine 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,ShardingKey:%s\n",
$message->getMessageId(), $message->getMessageTag(), $message->getMessageBody(),
$message->getPublishTime(), $message->getFirstConsumeTime(), $message->getConsumedTimes(), $message->getNextConsumeTime(),
$message->getShardingKey());
print_r($message->getProperties());
}
// ACK all messages. If the broker does not receive an ACK before
// getNextConsumeTime(), it redelivers the message. Each redelivery
// assigns a new receipt handle with an updated timestamp.
print_r($receiptHandles);
$this->ackMessages($receiptHandles);
print "=======>ack finish\n";
}
}
}
$instance = new ConsumerTest();
$instance->run();
?>
Send ordered messages bölümündeki yer tutuculara ek olarak aşağıdakileri de değiştirin:
| Yer Tutucu | Açıklama |
|---|---|
${GROUP_ID} |
Tüketici grubu kimliği |
consumeMessageOrderly() parametreleri
| Parametre | Açıklama | Aralık |
|---|---|---|
numOfMessages |
Toplu işlem başına tüketilecek maksimum mesaj sayısı | 16'ya kadar |
waitSeconds |
Saniye cinsinden uzun yoklama süresi. Mesaj yoksa istek, bir mesaj gelene veya zaman aşımı dolana kadar aracıda bekletilir | 30'a kadar |
Hata işleme
Tüketici üç hata senaryosunu işler:
| Özel Durum | Neden | Eylem |
|---|---|---|
MessageResolveException |
Mesaj gövdesindeki geçersiz karakterler ayrıştırmayı engeller | Hem ayrıştırılabilir hem de ayrıştırılamayan mesajları onaylayın (ACK) |
MessageNotExistException |
Konuda kullanılabilir mesaj yok | Uzun yoklamayı otomatik olarak sürdürün |
AckMessageException |
ACK gönderilmeden önce makbuz tanıtıcısının süresi doldu | Hatayı günlüğe kaydedin ve sonraki iletimde yeniden deneyin |