RabbitMQ Streams
A log inside RabbitMQ
Section titled “A log inside RabbitMQ”The previous lesson framed RabbitMQ and Kafka as opposites — a delete-on-ack queue versus a retained log. RabbitMQ Streams blur that line: they bring a Kafka-like append-only log into RabbitMQ, so you can get replay and high fan-out without adding a second system.
A stream is a queue type (declared with x-queue-type: stream), but it behaves very differently from a classic queue:
- Messages are appended to a log and retained, not deleted when read.
- Reads are non-destructive — consuming does not remove anything.
- Each consumer reads from an offset it controls, and can start from the beginning, the end, a timestamp, or a saved position.
- Many consumers can read the same stream independently and at different speeds.
flowchart LR prod["Producer"] -->|append| log["Stream log [0][1][2][3][4][5]"] log --> c1["Consumer A offset 1 (replaying)"] log --> c2["Consumer B offset 5 (live)"] log --> c3["Consumer C from timestamp"]
Stream versus classic queue
Section titled “Stream versus classic queue”| Classic queue | Stream | |
|---|---|---|
| On read | Removed after ack | Retained (non-destructive) |
| Replay | No | Yes — reset the offset |
| Multiple readers of same data | Compete for messages | Each reads the whole log independently |
| Retention | Until consumed | Time or size based (e.g. keep 7 days) |
| Best at | Task distribution, per-message work | Event fan-out, replay, large-scale reads |
Because a stream keeps data, you declare retention — by time or total size — rather than relying on consumption to clear it. Old segments are truncated once they age out.
When to use a stream
Section titled “When to use a stream”Choose a stream when you want a Kafka-like log but would rather not run Kafka: you need replay, you have many consumers reading the same events, or you’re ingesting a high-throughput firehose and want time-based retention. There’s also a super stream — a partitioned stream — for scaling a single logical stream across nodes, much like Kafka partitions.
Stay with a classic (or quorum) queue when the message is a task done once and discarded, when you need per-message routing and retry, or when you don’t need history. Most task-distribution and RPC work still wants a queue, not a stream.