Mock NATS queue group load balancing in Python

Simulates a NATS queue group where each message is delivered to exactly one subscriber using random selection with a lightweight mock.

Easy Python 3.9+ Aug 9, 2026 Streaming & messaging 13 views 0 copies

Python code

36 lines
Python 3.9+
import random
import time
from collections import defaultdict


class MockQueueGroup:
    """Mock a NATS queue group: each message is delivered to exactly one subscriber."""

    def __init__(self, subscribers):
        self.subscribers = subscribers

    def publish(self, message):
        receiver = random.choice(self.subscribers)
        time.sleep(0.01)  # simulate processing time
        return receiver

    def run_demo(self, messages, message_label="Task"):
        deliveries = defaultdict(int)
        for idx, msg in enumerate(messages, start=1):
            worker = self.publish(msg)
            deliveries[worker] += 1
            print(f"{message_label} #{idx} -> {worker} handled: {msg}")

        print("\nDelivery distribution after load balancing:")
        total = len(messages)
        for worker in self.subscribers:
            count = deliveries[worker]
            percent = (count / total) * 100
            print(f"{worker}: {count} messages ({percent:.1f}%)")


if __name__ == "__main__":
    workers = ["worker-A", "worker-B", "worker-C"]
    tasks = list(range(1, 21))  # 20 tasks to load balance
    queue_group = MockQueueGroup(workers)
    queue_group.run_demo(tasks)

Output

stdout
Task #1 -> worker-B handled: 1
Task #2 -> worker-C handled: 2
Task #3 -> worker-A handled: 3
Task #4 -> worker-B handled: 4
Task #5 -> worker-A handled: 5
Task #6 -> worker-C handled: 6
Task #7 -> worker-A handled: 7
Task #8 -> worker-B handled: 8
Task #9 -> worker-C handled: 9
Task #10 -> worker-A handled: 10
Task #11 -> worker-B handled: 11
Task #12 -> worker-C handled: 12
Task #13 -> worker-A handled: 13
Task #14 -> worker-B handled: 14
Task #15 -> worker-C handled: 15
Task #16 -> worker-A handled: 16
Task #17 -> worker-B handled: 17
Task #18 -> worker-C handled: 18
Task #19 -> worker-A handled: 19
Task #20 -> worker-C handled: 20

Delivery distribution after load balancing:
worker-A: 7 messages (35.0%)
worker-B: 7 messages (35.0%)
worker-C: 6 messages (30.0%)

How it works

The MockQueueGroup class mimics the behavior of a NATS queue group by using random.choice to assign each message to one of the subscriber workers. A defaultdict tracks the number of messages each worker receives, allowing the demo to print a distribution breakdown. The time.sleep(0.01) call simulates processing delay without blocking real queues, making the mock useful for unit tests and quick experiments. This pattern is valid because NATS queue groups are designed to load balance messages across subscribers, and the random selection approximates the even distribution of a real broker.

Common mistakes

  • Forgetting to import `defaultdict` from collections
  • Assuming a fixed receiver instead of using random.choice for each message
  • Not accounting for the case where total messages is zero, causing division by zero

Variations

  1. Use a round-robin counter instead of random.choice for deterministic load balancing
  2. Replace the mock with the actual `nats-py` library to connect to a real NATS server

Real-world use cases

  • Unit testing application code that depends on NATS queue groups without spinning up a broker
  • Simulating message load distribution during performance testing of consumer logic
  • Prototyping event-driven systems where multiple workers consume from a shared subject

Sponsored

Run this sample

Open the browser IDE to tweak the example and see results without installing anything.

Open editor

More from Streaming & messaging

Related tutorials and quizzes for this topic.