Tüm Ürünler
Search
Belge Merkezi

ApsaraMQ for RocketMQ:Send and receive transactional messages

Son Güncelleme:Oct 04, 2026

ApsaraMQ for RocketMQ, dağıtılmış işlemler arasında nihai tutarlılığı sağlamak için eXtended Architecture (X/Open XA) protokolüne benzer şekilde işlemsel mesajlar kullanır. Aşağıdaki Node.js örnek kod, HTTP istemci SDK'sı aracılığıyla işlemsel mesajların nasıl gönderilip alınacağını gösterir.

İşlemsel mesajların çalışma şekli

İşlemsel bir mesaj, yerel bir işlemi mesaj teslimatına bağlayan iki aşamalı commit sürecinden geçer. Aşağıdaki adımlar, üretici, broker ve tüketiciler arasındaki tam etkileşimi açıklar:

  1. Üretici yarım mesajı broker'a gönderir. Yarım mesaj depolanır ancak tüketiciler tarafından görünmez kalır.

  2. Broker yarım mesajı kalıcı hale getirir ve bir onay numarası içeren bir onay (ACK) döndürür.

  3. Üretici yerel işlemi yürütür (örneğin, bir veritabanını günceller).

  4. Yerel işlemin sonucuna bağlı olarak üretici, onay numarasını kullanarak yarım mesajı commit eder veya geri alır.

  5. Üretici yarım mesajı commit ederse broker bunu teslim edilebilir olarak işaretler. Tüketiciler artık bu mesajı alabilir.

  6. Üretici yarım mesajı geri alırsa broker mesajı atar. Tüketiciler mesajı asla görmez.

  7. Broker bir commit veya geri alma kararı almazsa (ağ arızası veya uygulama yeniden başlatma nedeniyle), TransCheckImmunityTime aralığı dolduktan sonra bir işlem kontrolü başlatır.

  8. Üretici kontrol isteğini alır ve yerel işlem durumunu yeniden değerlendirir.

  9. Üretici commit veya geri alma kararını broker'a gönderir. Karar hala bilinmiyorsa broker 24 saat boyunca her 10 saniyede bir yeniden dener.

图片1.png

Daha fazla bilgi için bkz. Transactional messages.

HTTP SDK ile ilgili hususlar

HTTP istemci SDK'sı işlem kontrolleri için çekme tabanlı bir model kullanır. Broker'ın kontrol isteklerini üreticiye ittiği yerel RocketMQ SDK'larının aksine HTTP SDK'sı, üreticinin consumeHalfMessage ile commit edilmemiş yarım mesajları yoklamasını gerektirir. Güvenilir işlem çözümlemesi sağlamak için bu yoklama döngüsünü ayrı bir işlemde veya iş parçacığında çalıştırın.

Ön koşullar

  • Node.js SDK'yı yükleyin. Daha fazla bilgi için bkz. Prepare the environment.

  • ApsaraMQ for RocketMQ konsolunda gerekli kaynakları oluşturun: bir örnek, bir konu ve bir tüketici grubu. Daha fazla bilgi için bkz. Create resources.

  • Alibaba Cloud hesabınız için bir AccessKey çifti edinin. Daha fazla bilgi için bkz. Create an AccessKey pair.

Temel parametreler

Örnek kodu çalıştırmadan önce aşağıdaki yer tutucuları gerçek değerlerinizle değiştirin:

Yer Tutucu Açıklama Nerede Bulunur
${HTTP_ENDPOINT} Örneğinizin HTTP uç noktası ApsaraMQ for RocketMQ konsolundaki Instance Details sayfası > HTTP Endpoint bölümü
${TOPIC} Konu adı ApsaraMQ for RocketMQ konsolunda oluşturuldu
${GROUP_ID} Tüketici grubu kimliği ApsaraMQ for RocketMQ konsolunda oluşturuldu
${INSTANCE_ID} Örnek kimliği (örneğin ad alanı yoksa null veya "" olarak ayarlayın) ApsaraMQ for RocketMQ konsolundaki Instance Details sayfası

AccessKey kimlik bilgileri ortam değişkenlerinden okunur:

  • ALIBABA_CLOUD_ACCESS_KEY_ID

  • ALIBABA_CLOUD_ACCESS_KEY_SECRET

İşlemsel mesajları gönderme

Üretici kodu iki bölümden oluşur: işlemsel mesajları gönderme ve commit edilmemiş yarım mesajları yoklama.

İşlem üreticisi API'si

Yöntem Amaç
client.getTransProducer(instanceId, topic, groupId) Bir işlem üreticisi oluşturma
mqTransProducer.publishMessage(body, tag, msgProps) Yarım mesaj gönderme
mqTransProducer.commit(receiptHandle) Yarım mesajı commit etme
mqTransProducer.rollback(receiptHandle) Yarım mesajı geri alma
mqTransProducer.consumeHalfMessage(numOfMessages, waitSeconds) Commit edilmemiş yarım mesajları yoklama

TransCheckImmunityTime

TransCheckImmunityTime parametresi, broker'ın yarım mesaj gönderildikten sonra ilk işlem kontrolünden önce ne kadar bekleyeceğini belirler.

  • Birim: saniye

  • Geçerli değerler: 10 ila 300

  • İlk kontrolden sonraki davranış: Yarım mesaj hala commit edilmemişse broker 24 saat boyunca her 10 saniyede bir kontrol eder.

Örnek kod

Aşağıdaki örnek dört işlemsel mesaj gönderir. İlk mesaj yayımlandıktan hemen sonra commit edilir. Kalan üç mesaj, özel özellikleri olan a değerine göre yarım mesaj yoklama döngüsü sırasında çözülür:

  • a=1: ilk kontrolde commit edilir

  • a=2: ikinci kontrolden sonra commit edilir (ConsumedTimes > 1)

  • a=3: geri alınır

const {
  MQClient,
  MessageProperties
} = require('@aliyunmq/mq-http-sdk');

// HTTP endpoint from the Instance Details page in the ApsaraMQ for RocketMQ console.
const endpoint = "${HTTP_ENDPOINT}";
// AccessKey credentials from environment variables.
const accessKeyId = process.env['ALIBABA_CLOUD_ACCESS_KEY_ID'];
const accessKeySecret = process.env['ALIBABA_CLOUD_ACCESS_KEY_SECRET'];

var client = new MQClient(endpoint, accessKeyId, accessKeySecret);

// Topic created in the ApsaraMQ for RocketMQ console.
const topic = "${TOPIC}";
// Consumer group ID created in the ApsaraMQ for RocketMQ console.
const groupId = "${GROUP_ID}";
// Instance ID. Set to null or "" if the instance has no namespace.
const instanceId = "${INSTANCE_ID}";

const mqTransProducer = client.getTransProducer(instanceId, topic, groupId);

async function processTransResult(res, msgId) {
    if (!res) {
        return;
    }
    if (res.code != 204) {
      // Commit or rollback failed. The receipt handle may have expired
      // if TransCheckImmunityTime or the consumeHalfMessage timeout elapsed.
      console.log("Commit/Rollback Message Fail:");
      const failHandles = res.body.map((error) => {
        console.log("\tErrorHandle:%s, Code:%s, Reason:%s\n", error.ReceiptHandle, error.ErrorCode, error.ErrorMessage);
        return error.ReceiptHandle;
      });
    } else {
      console.log("Commit/Rollback Message suc!!! %s", msgId);
    }
}

var halfMessageCount = 0;
var halfMessageConsumeCount = 0;

(async function(){
  try {
    // Send four transactional messages.
    for(var i = 0; i < 4; i++) {
      let res;
      msgProps = new MessageProperties();
      // Custom property used by the half-message polling loop
      // to decide whether to commit or roll back.
      msgProps.putProperty("a", i);
      msgProps.messageKey("MessageKey");
      // First transaction check delay: 10 seconds. Valid values: 10-300.
      // After the first check, the broker rechecks every 10 seconds for up to 24 hours.
      msgProps.transCheckImmunityTime(10);
      res = await mqTransProducer.publishMessage("hello mq.", "tagA", msgProps);
      console.log("Publish message: MessageID:%s,BodyMD5:%s,Handle:%s", res.body.MessageId, res.body.MessageBodyMD5, res.body.ReceiptHandle);
      if (res && i == 0) {
          // Commit the first message immediately using its receipt handle.
          const msgId = res.body.MessageId;
          res = await mqTransProducer.commit(res.body.ReceiptHandle);
          console.log("Commit msg when publish, %s", msgId);
        processTransResult(res, msgId);
      }
    }
  } catch(e) {
    // Add retry or persistence logic for failed messages.
    console.log(e)
  }
})();

// Run a separate loop to poll and resolve uncommitted half messages.
(async function() {
  // Poll for half messages, similar to consuming normal messages.
  while(halfMessageCount < 3 && halfMessageConsumeCount < 15) {
    try {
        halfMessageConsumeCount++;
      res = await mqTransProducer.consumeHalfMessage(3, 3);
      if (res.code == 200) {
        console.log("Consume Messages, requestId:%s", res.requestId);
        res.body.forEach(async (message) => {
          console.log("\tMessageId:%s,Tag:%s,PublishTime:%d,NextConsumeTime:%d,FirstConsumeTime:%d,ConsumedTimes:%d,Body:%s" +
            ",Props:%j,MessageKey:%s,Prop-A:%s",
              message.MessageId, message.MessageTag, message.PublishTime, message.NextConsumeTime, message.FirstConsumeTime, message.ConsumedTimes,
              message.MessageBody,message.Properties,message.MessageKey,message.Properties.a);

          var propA = message.Properties && message.Properties.a ? parseInt(message.Properties.a) : 0;
                  var opResp;
                  if (propA == 1 || (propA == 2 && message.ConsumedTimes > 1)) {
                      opResp = await mqTransProducer.commit(message.ReceiptHandle);
                      console.log("Commit msg when check half, %s", message.MessageId);
                      halfMessageCount++;
                  } else if (propA == 3) {
                      opResp = await mqTransProducer.rollback(message.ReceiptHandle);
                      console.log("Rollback msg when check half, %s", message.MessageId);
                      halfMessageCount++;
                  }
                  processTransResult(opResp, message.MessageId);
        });
      }
    } catch(e) {
      if (e.Code && e.Code.indexOf("MessageNotExist") > -1) {
        // No half messages available. Long polling continues.
        console.log("Consume Transaction Half msg: no new message, RequestId:%s, Code:%s", e.RequestId, e.Code);
      } else {
        console.log(e);
      }
    }
  }
})();

Örnek kodun çalışma şekli

Kod iki eşzamanlı async işlevi çalıştırır:

  1. Gönderme döngüsü: Etiket tagA ve döngü dizinine (0-3) ayarlanmış a adlı özel bir özellik ile dört yarım mesaj yayınlar. İlk mesaj (a=0), yayımlandıktan hemen sonra commit edilir.

  2. Yarım mesaj yoklama döngüsü: Commit edilmemiş yarım mesajları yoklar ve a özelliğine göre bunları çözümler. Döngü, 3 yarım mesajı çözümledikten veya 15 yoklama girişiminden sonra sonlanır.

    **Özellik a değeri** Eylem Koşul
    0 Gönderim sırasında zaten commit edildi N/A
    1 Commit İlk kontrol
    2 Commit Yeniden denemeden sonra (ConsumedTimes > 1)
    3 Geri alma İlk kontrol

Yanıt kodları

Kod Anlamı
200 Mesajlar alındı
204 Commit, geri alma veya ACK başarılı oldu
204 dışı İşlem başarısız oldu (onay numarasının süresi dolmuş olabilir)

İşlemsel mesajları alma

Tüketiciler, commit edilmiş işlemsel mesajları normal mesajlarla aynı şekilde alır. Uzun yoklama, bir mesaj gelene veya zaman aşımı dolana kadar isteği askıya alarak meşgul beklemeyi önler.

Tüketici API'si

Yöntem Amaç
client.getConsumer(instanceId, topic, groupId) Bir tüketici oluşturma
consumer.consumeMessage(numOfMessages, waitSeconds) Uzun yoklama kullanarak mesajları çekme
consumer.ackMessage(handles) Tüketilen mesajları onaylama

Parametreler

Parametre Açıklama Maksimum Değer
numOfMessages İstek başına alınacak maksimum mesaj sayısı 16
waitSeconds Saniye cinsinden uzun yoklama süresi 30

Örnek kod

const {
  MQClient
} = require('@aliyunmq/mq-http-sdk');

// HTTP endpoint from the Instance Details page in the ApsaraMQ for RocketMQ console.
const endpoint = "${HTTP_ENDPOINT}";
// AccessKey credentials from environment variables.
const accessKeyId = process.env['ALIBABA_CLOUD_ACCESS_KEY_ID'];
const accessKeySecret = process.env['ALIBABA_CLOUD_ACCESS_KEY_SECRET'];

var client = new MQClient(endpoint, accessKeyId, accessKeySecret);

// Topic created in the ApsaraMQ for RocketMQ console.
const topic = "${TOPIC}";
// Consumer group ID created in the ApsaraMQ for RocketMQ console.
const groupId = "${GROUP_ID}";
// Instance ID. Set to null or "" if the instance has no namespace.
const instanceId = "${INSTANCE_ID}";

const consumer = client.getConsumer(instanceId, topic, groupId);

(async function(){
  // Consume messages in a loop.
  while(true) {
    try {
      // Long polling: if no message is available, the request suspends on the broker
      // until a message arrives or the timeout elapses.
      res = await consumer.consumeMessage(
          3, // Max messages per request (up to 16)
          3  // Long polling timeout in seconds (up to 30)
          );

      if (res.code == 200) {
        console.log("Consume Messages, requestId:%s", res.requestId);
        const handles = res.body.map((message) => {
          console.log("\tMessageId:%s,Tag:%s,PublishTime:%d,NextConsumeTime:%d,FirstConsumeTime:%d,ConsumedTimes:%d,Body:%s" +
            ",Props:%j,MessageKey:%s,Prop-A:%s",
              message.MessageId, message.MessageTag, message.PublishTime, message.NextConsumeTime, message.FirstConsumeTime, message.ConsumedTimes,
              message.MessageBody,message.Properties,message.MessageKey,message.Properties.a);
          return message.ReceiptHandle;
        });

        // ACK all processed messages. If the broker does not receive an ACK
        // before NextConsumeTime, it redelivers the message with a new receipt handle.
        res = await consumer.ackMessage(handles);
        if (res.code != 204) {
          // ACK failed. The receipt handle may have expired.
          console.log("Ack Message Fail:");
          const failHandles = res.body.map((error)=>{
            console.log("\tErrorHandle:%s, Code:%s, Reason:%s\n", error.ReceiptHandle, error.ErrorCode, error.ErrorMessage);
            return error.ReceiptHandle;
          });
          handles.forEach((handle)=>{
            if (failHandles.indexOf(handle) < 0) {
              console.log("\tSucHandle:%s\n", handle);
            }
          });
        } else {
          console.log("Ack Message suc, RequestId:%s\n\t", res.requestId, handles.join(','));
        }
      }
    } catch(e) {
      if (e.Code.indexOf("MessageNotExist") > -1) {
        // No messages available. Long polling continues.
        console.log("Consume Message: no new message, RequestId:%s, Code:%s", e.RequestId, e.Code);
      } else {
        console.log(e);
      }
    }
  }
})();

Tüketicinin çalışma şekli

Tüketici şunları yapan sonsuz bir döngü çalıştırır:

  1. 3 saniyelik uzun yoklama zaman aşımı ile istek başına en fazla 3 mesaj çeker.

  2. Her mesajı işler ve onay numaralarını toplar.

  3. İşlenen tüm mesajlar için bir ACK gönderir.

Tüketici NextConsumeTime öncesinde bir mesajı ACK yapmazsa broker mesajı yeniden teslim eder. Her yeniden teslim yeni bir onay numarası oluşturur.

Mesaj mevcut olmadığında MessageNotExist hata kodu döndürülür. Bu, uzun yoklama modunda normaldir; döngü beklemeye devam eder.

Kullanım notları

  • Hemen commit edin veya geri alın. Çözülmemiş yarım mesajlar broker depolamasını tüketir. İşlem gerçekten hala devam etmiyorsa yerel işlem kontrolünüzden bilinmeyen bir durum döndürmekten kaçının.

  • Onay numarası süre sonlarını yönetin. Onay numaralarının sınırlı bir geçerlilik süresi vardır. Commit veya geri alma işlemi 204 dışı bir yanıtla başarısız olursa numaranın süresi dolmuş olabilir. Broker otomatik olarak bir işlem kontrolünü yeniden tetikler.

  • Yarım mesaj yoklama döngüsünü güvenilir şekilde çalıştırın. Üretim ortamında çözülmemiş yarım mesajların zamanında işlenmesini sağlamak için consumeHalfMessage döngüsünü ayrı bir uzun süre çalışan işlem olarak çalıştırın.

Sonraki adımlar