Tous les produits
Search
Centre de documentation

ApsaraMQ for RocketMQ:Send and receive ordered messages by using the HTTP client SDK for Node.js

Dernière mise à jour :Aug 09, 2026

Les messages ordonnés dans ApsaraMQ for RocketMQ garantissent une distribution stricte selon le principe premier entré, premier sorti (FIFO). Utilisez les messages ordonnés lorsque le respect de la séquence est crucial pour votre logique métier, par exemple pour l'appariement de transactions où l'offre la plus ancienne à un prix donné doit l'emporter, ou pour la synchronisation des modifications de base de données où les insertions, mises à jour et suppressions doivent être rejouées dans l'ordre.

Cette rubrique explique comment envoyer et recevoir des messages ordonnés avec le package @aliyunmq/mq-http-sdk pour Node.js.

Fonctionnement de l'ordonnancement

ApsaraMQ for RocketMQ prend en charge deux périmètres d'ordonnancement :

  • Messages globalement ordonnés : tous les messages d'un topic sont distribués et consommés selon une seule séquence FIFO.

  • Messages ordonnés par partition : les messages sont répartis entre plusieurs partitions via une clé de sharding. Au sein de chaque partition, les messages sont distribués et consommés dans l'ordre FIFO. Les messages appartenant à différentes partitions peuvent être consommés simultanément.

Une clé de sharding détermine la partition vers laquelle un message est routé. Une clé de message constitue un index distinct utilisé pour la recherche de messages. Ne confondez pas ces deux concepts.

L'ordonnancement repose sur deux garanties indépendantes :

Garantie Exigence
Ordre de production Envoyez les messages depuis un seul producteur sur un unique thread. Si plusieurs producteurs ou threads envoient des messages simultanément, le broker enregistre les messages selon leur ordre d'arrivée, qui peut différer de l'ordre métier souhaité.
Ordre de consommation Accusez réception de chaque lot de messages avant de consommer le lot suivant provenant de la même partition. Si le broker ne reçoit pas d'accusé de réception (ACK), il redistribue le message non acquitté avant de livrer les nouveaux messages.

Prérequis

Avant de commencer, assurez-vous d'avoir :

Envoi de messages ordonnés

Important

L'ordre n'est garanti que lorsqu'un seul producteur envoie des messages sur un unique thread. Si plusieurs producteurs ou threads envoient des messages simultanément, le broker enregistre les messages selon leur ordre d'arrivée, qui peut différer de l'ordre métier souhaité.

Remplacez les espaces réservés suivants par vos valeurs réelles avant d'exécuter le code :

Espace réservé Description
${HTTP_ENDPOINT} Endpoint HTTP disponible sur la page Instance Details > section HTTP Endpoint dans la console ApsaraMQ for RocketMQ
${TOPIC} Nom du topic créé dans la console
${INSTANCE_ID} ID de l'instance. Si l'instance possède un namespace, spécifiez l'ID. Sinon, définissez la valeur sur null ou "". Consultez la page Instance Details pour vérifier la présence d'un namespace.
const {
  MQClient,
  MessageProperties
} = require('@aliyunmq/mq-http-sdk');

// HTTP endpoint. Get this from Instance Details > HTTP Endpoint in the console.
const endpoint = "${HTTP_ENDPOINT}";
// Load 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}";
// Instance ID. Set to null or "" if the instance does not have a namespace.
const instanceId = "${INSTANCE_ID}";

const producer = client.getProducer(instanceId, topic);

(async function(){
  try {
    // Send 8 messages. Sharding key i % 2 routes messages to 2 partitions.
    for(var i = 0; i < 8; i++) {
      msgProps = new MessageProperties();
      // Set a custom property.
      msgProps.putProperty("a", i);
      // Set the sharding key. Messages with the same key go to the same partition.
      msgProps.shardingKey(i % 2);
      // Publish with body "hello mq." and tag "TagA".
      res = await producer.publishMessage("hello mq.", "TagA", msgProps);
      console.log("Publish message: MessageID:%s,BodyMD5:%s", res.body.MessageId, res.body.MessageBodyMD5);
    }
  } catch(e) {
    // Handle send failures. Implement retry or persistence logic as needed.
    console.log(e)
  }
})();

Réception de messages ordonnés

Pour consommer les messages dans l'ordre, appelez consumeMessageOrderly() au lieu de consumeMessage(). Cette méthode garantit que les messages provenant de la même partition arrivent dans l'ordre d'envoi.

Comportements clés :

  • Un seul appel peut renvoyer des messages provenant de plusieurs partitions. L'ordre est préservé au sein de chaque partition.

  • Si le broker ne reçoit pas d'ACK, il redistribue ce message avant de livrer le suivant dans la même partition.

  • Vous devez accuser réception de tous les messages d'un lot avant que le broker ne livre le lot suivant provenant de la même partition.

  • Interrogation longue (long polling) : si aucun message n'est disponible, le broker maintient la connexion ouverte pendant la durée d'attente spécifiée et répond immédiatement dès qu'un message arrive.

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

// HTTP endpoint. Get this from Instance Details > HTTP Endpoint in the console.
const endpoint = "${HTTP_ENDPOINT}";
// Load 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 = "GID_http";
// Instance ID. Set to null or "" if the instance does not have a namespace.
const instanceId = "${INSTANCE_ID}";

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

(async function(){
  while(true) {
    try {
      // consumeMessageOrderly(numOfMessages, waitSeconds)
      //   numOfMessages: max messages per call (up to 16)
      //   waitSeconds:   long polling duration in seconds (up to 30)
      res = await consumer.consumeMessageOrderly(
          3, // Receive up to 3 messages per call.
          3  // Wait up to 3 seconds if no messages are available.
          );

      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,ShardingKey:%s,Prop-A:%s,Tag:%s",
              message.MessageId, message.MessageTag, message.PublishTime, message.NextConsumeTime, message.FirstConsumeTime, message.ConsumedTimes,
              message.MessageBody,message.Properties,message.ShardingKey,message.Properties.a);
          return message.ReceiptHandle;
        });

        // Acknowledge consumed messages.
        // If NextConsumeTime passes without an ACK, the broker redelivers the message.
        res = await consumer.ackMessage(handles);
        if (res.code != 204) {
          // ACK failed -- typically caused by an expired receipt handle.
          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);
      }
    }
  }
})();

Bonnes pratiques

  • Envoyez depuis un seul thread producteur. Plusieurs producteurs ou threads envoyant vers la même clé de sharding peuvent rompre l'ordre souhaité, car le broker enregistre les messages selon leur heure d'arrivée et non leur heure d'envoi.

  • Accusez réception de chaque lot avant de consommer le suivant. Le broker bloque la livraison du lot suivant pour une partition jusqu'à ce que tous les messages du lot actuel soient acquittés. Ignorer ou retarder les accusés de réception bloque la consommation.

Références