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.
Python code
36 linesimport 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
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
- Use a round-robin counter instead of random.choice for deterministic load balancing
- 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
More from Streaming & messaging
- At Most Once Fire-and-Forget Mock in Python easy
- Batch Consume Process Commit Pattern in Python medium
- Build a Streaming Messaging Helper in Python easy
- Dead Letter Queue Failed Messages List Mock in Python easy
- Dedupe processed message IDs in Python easy
- Event Envelope with Schema Version Field in Python easy
Keep learning
Related tutorials and quizzes for this topic.