This topic describes how to ensure the ordering of messages when you send messages to Message Service (MNS) queues or consume messages from MNS queues.

Background information

MNS queues provide high reliability, availability, and concurrency. Data of each queue is permanently stored in three copies in the Apsara Stack platform. A minimum of two servers provide services for each queue, and each server can process highly concurrent requests. The first in, first out (FIFO) strategy cannot be strictly implemented for queue messages due to the distributed services of MNS.

If multiple clients send messages to a queue at the same time, the ordering of messages cannot be ensured due to concurrent requests and network latency. In this case, the order in which the clients send messages and in which messages arrive at servers cannot be retrieved. When multiple clients receive messages at the same time, the order in which messages are processed cannot be retrieved either.

The ordering of messages can be ensured only when one sender client and one receiver client exist. In this case, the order in which the client sends messages and the order in which messages arrive at servers can be retrieved.

Solution

Based on the background information, MNS provides the following solution to ensure the ordering of messages when you send and consume queue messages:

  1. Add the SeqId parameter for messages on the sender client. The value of the SeqId parameter is in the #num# format.
  2. Retrieve and sort messages based on the SeqId parameter on the consumer client. A backend thread is used to ensure that a message is not repeatedly consumed.
  3. The values of the SeqId parameter are backed up to an on-premises disk file for persistent storage. This way, the values will not be lost if the sender or consumer client fails. You can also use other Alibaba Cloud services to store the values of the SeqId parameter, such as Object Storage Service (OSS), Tablestore, and ApsaraDB RDS databases.Flowchart

Usage notes

  • The sample code provided in this topic has not been strictly tested. We recommend that you test the sample code before you apply it to a production environment.
  • The values of the SeqId parameter on the sender client and consumer client match the messages in a queue. If you delete or create a queue, check whether the values of the SeqId parameter in the on-premises disk file match the messages in the queue. If a queue exclusively receives the messages whose SeqId parameter is specified, we recommend that you do not send the messages whose SeqId parameter is not specified to the queue.
  • The retention period of queue messages and the actual processing result may affect the ordering of messages. You need to use code to resolve the issue.

Sample code

Python sample code is used as an example. Click here to download the sample code. The sample code is supported by the MNS SDK for Python. The OrderedQueueWrapper class (the oredered_queue.py file) allows you to change an MNS queue into an ordered queue.

The OrderedQueueWrapper class provides the SendMessageInOrder() and ReceiveMessageInOrder() methods.

  • The SendMessageInOrder() method is used to send messages whose SeqId parameter is specified.
  • The ReceiveMessageInOrder() method is used to retrieve and sort messages on the consumer client.

The sample code that is used to send and receive messages is provided in the send_message_in_order.py and receive_message_in_order.py files in the OrderedQueueWrapper package.

send_message_in_order.py:
    #init orderedQueue
    seqIdConfig = {"localFileName":"/tmp/mns_send_message_seq_id"}  #Specify an on-premises disk file to permanently store values of the SeqId parameter.
    seqIdPS = LocalDiskStorage(seqIdConfig)
    orderedQueue = OrderedQueueWrapper(myQueue, sendSeqIdPersistStorage = seqIdPS)
    orderedQueue.SendMessageInOrder(message)

receive_message_in_order.py:
    #init orderedQueue
    seqIdConfig = {"localFileName":"/tmp/mns_receive_message_seq_id"} #Specify an on-premises disk file to persistently store values of the SeqId parameter.
    seqIdPS = LocalDiskStorage(seqIdConfig)
    orderedQueue = OrderedQueueWrapper(myQueue, receiveSeqIdPersistStorage = seqIdPS)
    recv_msg = orderedQueue.ReceiveMessageInOrder(wait_seconds)

Running methods

  1. Set the g_endpoint, g_accessKeyId, g_accessKeySecret, and g_testQueueName parameters in the send_message_in_order.py and receive_message_in_order.py files.
  2. Run the sample code in the send_message_in_order.py file.
    python send_message_in_order.py
  3. Run the sample code in the receive_message_in_order.py file.
    python receive_message_in_order.py

    The sender client sends 20 messages, and the receiver client consumes these 20 messages in order.

    Ordered consumption

You can also run the sample code in the oredered_queue.py file to compare an unordered queue and ordered queue. Run the following command:

python ordered_queue.py
Note You need to configure the endpoint and AccessKey pairs for the oredered_queue.py file.

Results

The following two results are returned.

  • Unordered queueUnordered queue

    Messages are not strictly ordered. This indicates that multiple servers process requests at the same time for a single MNS queue.

  • Ordered queueOrdered queue