Tüm Ürünler
Search
Belge Merkezi

ApsaraMQ for RocketMQ:Send and receive ordered messages

Son Güncelleme:Oct 04, 2026

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.

Önemli

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:

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