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

Background information

MNS queues feature high reliability, availability, and concurrency. Data of each queue is 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 orders 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.

Typically, the ordering of messages can be ensured when only one sender client (one process that can include one or more threads) and one receiver client exist. In this case, the orders in which the client send messages and 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 at sender clients. The syntax of the value for the SeqId parameter is #num#.
  2. Retrieve messages and sort messages based on the SeqId parameter at consumer clients. A background thread is used to ensure that a message is not repeatedly consumed.
  3. To prevent the loss of values of the SeqId parameter during data transmission when sender or consumer clients fail, the values of the SeqId parameter are backed up to an on-premises disk file for persistent storage. You can also use other Alibaba Cloud services to store values of the SeqId parameter, such as Object Storage Service (OSS), Table Store, and ApsaraDB RDS databases.Ensure ordering of messages

Precautions

  • The sample code provided in this topic has not been strictly tested. We recommend that you test the sample code before applying it to the production environment. Any suggestions are highly appreciated.
  • Values of the SeqId parameter at sender and consumer clients match messages in a queue. If you delete a queue and create a new queue, check whether values of the SeqId parameter in the on-premises disk file match messages in the new queue. We recommend that you do not send messages for which the SeqId parameter is not specified to a queue that exclusively receives the messages for which the SeqId parameter is specified.
  • The message retention period of queues and the actual processing result may affect the ordering of messages. You need to use the code to solve the problems.

Sample code

This topic provides the Python sample code. For more information about the sample code, see Python sample code for ordered queues. The sample code must depend on the MNS Python SDK. 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 for which the SeqId parameter is specified.
  • The ReceiveMessageInOrder() method is used to retrieve and sort messages at consumer clients.

The sample code 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 permanently store values of the SeqId parameter.
    seqIdPS = LocalDiskStorage(seqIdConfig)
    orderedQueue = OrderedQueueWrapper(myQueue, receiveSeqIdPersistStorage = seqIdPS)
    recv_msg = orderedQueue.ReceiveMessageInOrder(wait_seconds)

Running methods

  1. Configure the g_endpoint, g_accessKeyId, g_accessKeySecret, and g_testQueueName 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.
  3. Run the sample code in the receive_message_in_order.py file. You do not need to wait until the send_message_in_order.py program is completed. The sender program sends 20 messages, and the receiver program consumes these 20 messages in order.Receive messages in order

You can also run the sample code in the oredered_queue.py file (you need to configure the endpoint and AccessKey pairs) to compare a normal queue and ordered queue. Run the following command:

$python oredered_queue.py
  • Normal queue: Messages are not strictly ordered. This indicates that multiple servers are processing requests for a single MNS queue.Normal queue
  • Ordered queueOrdered queue