Point-to-point queues vs. pub/sub topics — what's the actual difference in delivery semantics?
Same broker, two opposite contracts: a queue splits work across competing consumers, while a topic gives every subscriber its own full copy of the stream.
LEARN BY DOINGPut your intuition to work3 steps
Point-to-point queues vs. pub/sub topics — what's the actual difference in delivery semantics?
Use the lesson’s examples and explanation to check your idea.
Personal reflection. Nothing is sent to an AI service.#Same broker, two completely different delivery contracts
"Queue" and "topic" get used loosely, but they encode a real structural difference: a queue delivers each message to exactly one consumer among a competing pool (point-to-point), while a topic delivers a copy of each message to every independent subscriber (publish/subscribe). You can build both patterns on the same broker — RabbitMQ, for instance — because the pattern lives in how you wire exchanges to queues, not in the broker's identity.
#Point-to-point: a work queue with competing consumers
In the point-to-point model, a single logical queue holds messages, and any number of consumer processes pull from it. The broker hands each message to exactly one of them — this is called competing consumers — which is exactly what you want for distributing work: five order-processing workers all read from orders.process, and each order gets handled once, by whichever worker happens to be free.
# producer.py — point-to-point: one message, one worker will get itimport pika, jsonconnection = pika.BlockingConnection(pika.ConnectionParameters(host="localhost"))channel = connection.channel()channel.queue_declare(queue="orders.process", durable=True)order = {"order_id": "A-1042", "sku": "WIDGET-9", "qty": 3}channel.basic_publish(exchange="", # default exchange routes by queue namerouting_key="orders.process",body=json.dumps(order),properties=pika.BasicProperties(delivery_mode=2), # persist to disk)connection.close()
# worker.py — run N copies of this; RabbitMQ round-robins deliveries across themimport pika, jsonconnection = pika.BlockingConnection(pika.ConnectionParameters(host="localhost"))channel = connection.channel()channel.queue_declare(queue="orders.process", durable=True)channel.basic_qos(prefetch_count=1) # don't hand a worker a new message until it acks the last onedef handle_order(ch, method, properties, body):order = json.loads(body)print(f"processing {order['order_id']}")ch.basic_ack(delivery_tag=method.delivery_tag)channel.basic_consume(queue="orders.process", on_message_callback=handle_order)channel.start_consuming()
Run three copies of worker.py and each order still gets processed exactly once, split across whichever workers are idle. That's the defining trait: the queue is shared capacity, not a broadcast channel.
#Pub/sub: a topic with independent subscribers
Pub/sub inverts that. Each subscriber gets its own full copy of the stream, and adding a second subscriber doesn't reduce what the first one sees. In RabbitMQ this is modeled with a fanout exchange bound to one queue per subscriber:
# publisher.py — pub/sub: every bound queue gets its own copyimport pika, jsonconnection = pika.BlockingConnection(pika.ConnectionParameters(host="localhost"))channel = connection.channel()channel.exchange_declare(exchange="order.events", exchange_type="fanout")event = {"type": "OrderPlaced", "order_id": "A-1042", "total": 79.99}channel.basic_publish(exchange="order.events", routing_key="", body=json.dumps(event))connection.close()
# email_subscriber.py — its own queue, its own full copy of every eventimport pikaconnection = pika.BlockingConnection(pika.ConnectionParameters(host="localhost"))channel = connection.channel()channel.exchange_declare(exchange="order.events", exchange_type="fanout")result = channel.queue_declare(queue="", exclusive=True) # anonymous queue for this subscriberchannel.queue_bind(exchange="order.events", queue=result.method.queue)def send_confirmation_email(ch, method, properties, body):print(f"[email-service] sending confirmation for {body}")channel.basic_consume(queue=result.method.queue, on_message_callback=send_confirmation_email, auto_ack=True)channel.start_consuming()
# analytics_subscriber.py — a completely independent subscriber, same eventsimport pikaconnection = pika.BlockingConnection(pika.ConnectionParameters(host="localhost"))channel = connection.channel()channel.exchange_declare(exchange="order.events", exchange_type="fanout")result = channel.queue_declare(queue="", exclusive=True)channel.queue_bind(exchange="order.events", queue=result.method.queue)def record_metric(ch, method, properties, body):print(f"[analytics-service] recording sale event {body}")channel.basic_consume(queue=result.method.queue, on_message_callback=record_metric, auto_ack=True)channel.start_consuming()
Both email_subscriber.py and analytics_subscriber.py receive every OrderPlaced event, independently of each other. Kill one, restart it later, and — unless its queue was declared durable and it missed messages while down — it simply resumes seeing new events; it never "steals" a message meant for the other.
#Why this distinction matters more than the tool
| Point-to-point (queue) | Pub/sub (topic) | |
|---|---|---|
| Who gets each message | Exactly one consumer from the pool | Every subscriber, independently |
| Adding a consumer | Spreads the same total work thinner | Adds a new, full copy of the stream |
| Use case | Distributing work (jobs, tasks) | Broadcasting facts (events) |
| Kafka analogy | Consumers in the same consumer group | Consumers in different consumer groups |
That Kafka analogy is worth sitting with: Kafka doesn't have separate "queue" and "topic" primitives the way RabbitMQ does. Every Kafka topic is really a pub/sub log, but consumer groups let you get point-to-point semantics on top of it — every group gets a full independent copy of the topic (pub/sub across groups), while within a single group, each partition is consumed by only one member (point-to-point across group members). Same underlying log, both delivery contracts, depending on how consumers are grouped.
🧠 Check your understanding
0/2 · 0/2 answered1. Three consumer processes are all reading from the same RabbitMQ queue. A producer publishes 30 messages. Roughly how many messages does each consumer process?
2. In Kafka, how do you get 'point-to-point'-style delivery on top of a topic that's fundamentally pub/sub?