Backend Development 11 min read

7 Common Message Queue Scenarios and Their Implementations

This article explains seven typical message‑queue use cases—including ordinary, ordered, delayed, transactional, trace, dead‑letter, and priority messages—detailing their business motivations, implementation challenges, and concrete code examples for Kafka, RocketMQ, Pulsar, and RabbitMQ.

Wukong Talks Architecture
Wukong Talks Architecture
Wukong Talks Architecture
7 Common Message Queue Scenarios and Their Implementations

Hello, I am Wukong. When selecting a message‑queue solution we usually consider the business scenario. Below are seven common message scenarios and how they are implemented in mainstream MQ products.

1. Ordinary Message

The basic function of a message queue is to let producers send messages, brokers store them, and consumers retrieve them, achieving system decoupling and peak‑shaving.

2. Ordered Message

Ordered messages require the sending order of the producer to match the consumption order of the consumer, e.g., order creation → payment → shipment in e‑commerce.

Challenges include multiple producers, multiple partitions, and concurrent consumers, which can break ordering.

To guarantee order, two conditions must be met:

The same producer must synchronously send messages to the same partition.

A partition can be consumed by only one consumer.

Kafka and Pulsar assign a key to each message and hash it to a partition. RocketMQ can use MessageQueueSelector to choose a queue:

public static void main(String[] args) throws UnsupportedEncodingException {
    try {
        DefaultMQProducer producer = new DefaultMQProducer("please_rename_unique_group_name");
        producer.start();
        String[] tags = new String[]{"TagA", "TagB", "TagC", "TagD", "TagE"};
        for (int i = 0; i < 100; i++) {
            int orderId = i % 10;
            Message msg = new Message("TopicTestjjj", tags[i % tags.length], "KEY" + i,
                ("Hello RocketMQ " + i).getBytes(RemotingHelper.DEFAULT_CHARSET));
            SendResult sendResult = producer.send(msg, new MessageQueueSelector() {
                @Override
                public MessageQueue select(List<MessageQueue> mqs, Message msg, Object arg) {
                    Integer id = (Integer) arg;
                    int index = id % mqs.size();
                    return mqs.get(index);
                }
            }, orderId);
            System.out.printf("%s%n", sendResult);
        }
        producer.shutdown();
    } catch (MQClientException | RemotingException | MQBrokerException | InterruptedException e) {
        e.printStackTrace();
    }
}

RabbitMQ uses an exchange and a routing key to route messages:

@Resource
private AmqpTemplate rabbitTemplate;

public void send1(String message) {
    rabbitTemplate.convertAndSend("testExchange", "testRoutingKey", message);
}

3. Delayed (Scheduled) Message

Delayed messages are not consumed immediately but after a specified time, e.g., canceling an unpaid order after 30 minutes.

3.1 RocketMQ Implementation

RocketMQ defines 18 delay levels; level 3 corresponds to a 10‑second delay.

private String messageDelayLevel = "1s 5s 10s 30s 1m 2m 3m 4m 5m 6m 7m 8m 9m 10m 20m 30m 1h 2h";

Messages are first stored in a special topic (SCHEDULE_TOPIC_XXXX) and later moved to the original queue when the delay expires.

3.2 Pulsar Implementation

Pulsar stores delayed messages in a Delayed Message Tracker, which builds a priority queue based on the delay time. Consumers check the tracker for expired messages before pulling.

3.3 RabbitMQ Implementation

RabbitMQ can implement delayed messages either by routing expired messages to a dead‑letter queue or by storing them in a local Mnesia database and publishing them later.

3.4 Kafka Implementation

Kafka does not have a native delay queue, but delayed delivery can be achieved with producer interceptors or by using a dedicated delay topic.

4. Transactional Message

Transactional messages ensure that producing and consuming messages obey transaction semantics.

RabbitMQ and Kafka support producer‑side transactions (all‑or‑nothing send). RabbitMQ uses a channel to start a transaction:

ConnectionFactory factory = new ConnectionFactory();
connection = factory.newConnection();
Channel channel = connection.createChannel();
// start transaction
channel.txSelect();
channel.basicPublish("directTransactionExchange", "transactionRoutingKey", null, message.getBytes("utf-8"));
// commit or rollback
channel.txCommit();

Kafka can assign the same transaction ID to multiple producers, allowing atomic writes across topics and partitions.

Pulsar defines a transaction that can encompass consume‑process‑produce as a single atomic operation.

RocketMQ implements transactional messages using half messages; it guarantees atomicity between the local transaction and the message send, but not consumer‑side atomicity.

5. Trace Message

Trace messages record the lifecycle of a message to help diagnose loss. They require storage and query capabilities and should not impact normal message performance.

RabbitMQ can enable tracing to send trace data to the amq.rabbitmq.trace exchange. RocketMQ also supports trace messages but they are disabled by default.

6. Dead‑Letter Queue

Dead‑letter queues handle abnormal situations such as rejected messages, expiration, or queue length limits.

RocketMQ provides a consumer‑side dead‑letter queue after 16 failed retries. RabbitMQ supports both producer‑ and broker‑side dead‑letter queues, routing messages to a dead‑letter exchange.

7. Priority Message

Some business scenarios require higher‑priority processing, e.g., premium customers in banking.

RabbitMQ supports priority queues:

ConnectionFactory factory = new ConnectionFactory();
connection = factory.newConnection();
Channel channel = connection.createChannel();
Map
args = new HashMap
();
// set max priority to 5
args.put("x-max-priority", 5);
channel.queueDeclare("my-priority-queue", true, false, false, args);

8. Summary

When choosing a message‑queue solution, consider business scenarios, operational complexity, scale, community activity, and learning cost. The seven scenarios above provide a practical guide for selecting and configuring the right MQ technology.

Backenddistributed systemsKafkaMessage QueueRabbitMQrocketmqPulsar
Wukong Talks Architecture
Written by

Wukong Talks Architecture

Explaining distributed systems and architecture through stories. Author of the "JVM Performance Tuning in Practice" column, open-source author of "Spring Cloud in Practice PassJava", and independently developed a PMP practice quiz mini-program.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.