Message Brokers Comparison: RabbitMQ vs Kafka vs ActiveMQ
A comprehensive comparison of three popular message brokers for distributed systems.
Overview
┌──────────────────────────────────────────────────────────────────┐
│ Message Broker Landscape │
├──────────────┬────────────────────┬────────────────────────────────┤
│ RabbitMQ │ Kafka │ ActiveMQ │
├──────────────┼────────────────────┼────────────────────────────────┤
│ Traditional │ Distributed │ JMS-compliant │
│ Message │ Streaming │ Enterprise │
│ Broker │ Platform │ Message Broker │
└──────────────┴────────────────────┴────────────────────────────────┘
RabbitMQ
Architecture
┌──────────────────────────────────────────────────┐
│ RabbitMQ │
├──────────────────────────────────────────────────┤
│ │
│ Producer ──▶ Exchange ──▶ Queue ──▶ Consumer │
│ │ │
│ Bindings │
│ (routing rules) │
│ │
└──────────────────────────────────────────────────┘
Key Features
- AMQP Protocol: Standard messaging protocol
- Exchange Types: Direct, Topic, Fanout, Headers
- Message Acknowledgments: Reliable delivery
- Flexible Routing: Complex routing patterns
- Plugins: Extensive plugin ecosystem
- Management UI: Built-in web interface
Exchange Types
Direct Exchange:
Producer ──"order.created"──▶ Exchange ──▶ Queue (order.created)
Topic Exchange:
Producer ──"order.*.created"──▶ Exchange ──▶ Queue (order.usa.created)
──▶ Queue (order.eu.created)
Fanout Exchange:
Producer ──message──▶ Exchange ──▶ All bound queues
C# Example (using RabbitMQ.Client)
// Producer
var factory = new ConnectionFactory { HostName = "localhost" };
using var connection = factory.CreateConnection();
using var channel = connection.CreateModel();
channel.ExchangeDeclare("orders", ExchangeType.Topic, durable: true);
channel.QueueDeclare("order-processing", durable: true, exclusive: false);
channel.QueueBind("order-processing", "orders", "order.created");
var body = Encoding.UTF8.GetBytes(JsonSerializer.Serialize(order));
channel.BasicPublish(
exchange: "orders",
routingKey: "order.created",
body: body
);
// Consumer
channel.BasicConsume("order-processing", autoAck: false, consumer: new EventingBasicConsumer(channel)
{
Received = (model, ea) =>
{
var body = ea.Body.ToArray();
var message = Encoding.UTF8.GetString(body);
ProcessOrder(message);
channel.BasicAck(ea.DeliveryTag, multiple: false);
}
});
Best For
- Complex routing requirements
- Request/Reply patterns
- Traditional message queuing
- RPC-style communication
- Guaranteed delivery with acknowledgments
Apache Kafka
Architecture
┌──────────────────────────────────────────────────────────────┐
│ Kafka Cluster │
├──────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ Topic: orders │ │
│ ├──────────┬──────────┬──────────┬──────────────────┬─┤ │
│ │Partition │Partition │Partition │ ... │ │ │
│ │ 0 │ 1 │ 2 │ │ │ │
│ └──────────┴──────────┴──────────┴──────────────────┴─┘ │
│ │
│ Producers ────▶ Partitions ────▶ Consumer Groups │
│ │
└──────────────────────────────────────────────────────────────┘
Key Features
- High Throughput: Millions of messages per second
- Distributed Log: Append-only, immutable
- Partitioning: Horizontal scalability
- Replication: Fault tolerance
- Consumer Groups: Parallel processing
- Long Retention: Event sourcing capable
- Stream Processing: Kafka Streams, ksqlDB
Partition Strategy
┌─────────────────────────────────────────────────────┐
│ Partition Assignment │
├─────────────────────────────────────────────────────┤
│ │
│ Key-based: hash(order_id) % num_partitions │
│ │
│ Order 123 ──▶ Partition 0 │
│ Order 456 ──▶ Partition 1 │
│ Order 789 ──▶ Partition 2 │
│ │
│ Guarantees: Same key = Same partition = Ordering │
│ │
└─────────────────────────────────────────────────────┘
C# Example (using Confluent.Kafka)
// Producer
var config = new ProducerConfig { BootstrapServers = "localhost:9092" };
using var producer = new ProducerBuilder<string, string>(config).Build();
await producer.ProduceAsync("orders", new Message<string, string>
{
Key = order.Id, // Ensures ordering per order
Value = JsonSerializer.Serialize(order)
});
// Consumer
var consumerConfig = new ConsumerConfig
{
BootstrapServers = "localhost:9092",
GroupId = "order-processors",
AutoOffsetReset = AutoOffsetReset.Earliest
};
using var consumer = new ConsumerBuilder<string, string>(consumerConfig).Build();
consumer.Subscribe("orders");
while (true)
{
var message = consumer.Consume();
ProcessOrder(message.Value);
consumer.Commit(message);
}
Best For
- High-throughput event streaming
- Event sourcing architectures
- Log aggregation
- Real-time analytics
- Replay of historical events
- Microservices event bus
ActiveMQ
Architecture
┌──────────────────────────────────────────────────┐
│ ActiveMQ │
├──────────────────────────────────────────────────┤
│ │
│ JMS Producer ──▶ Destination ──▶ JMS Consumer │
│ │ │
│ Queue (P2P) │
│ Topic (Pub/Sub) │
│ │
└──────────────────────────────────────────────────┘
Key Features
- JMS Compliance: Java enterprise standard
- Multiple Protocols: AMQP, STOMP, OpenWire, MQTT
- Persistence: KahaDB, JDBC
- Clustering: Network of brokers
- Virtual Destinations: Composite destinations
- Message Groups: Related message processing
Destination Types
Queue (Point-to-Point):
Producer ──▶ Queue ──▶ Single Consumer
Topic (Publish/Subscribe):
Publisher ──▶ Topic ──▶ Subscriber 1
──▶ Subscriber 2
──▶ Subscriber N
Configuration Example
<!-- activemq.xml -->
<broker xmlns="http://activemq.apache.org/schema/core" brokerName="localhost">
<destinationPolicy>
<policyMap>
<policyEntries>
<policyEntry queue="orders.*" producerFlowControl="true"/>
</policyEntries>
</policyMap>
</destinationPolicy>
<persistenceAdapter>
<kahaDB directory="${activemq.data}/kahadb"/>
</persistenceAdapter>
</broker>
Best For
- JMS-based enterprise applications
- Legacy system integration
- Multi-protocol support requirements
- Traditional enterprise messaging
Comparison Matrix
| Feature | RabbitMQ | Kafka | ActiveMQ |
|---|---|---|---|
| Throughput | Medium (~50k/s) | Very High (~1M/s) | Medium (~50k/s) |
| Latency | Low (ms) | Medium (ms) | Low (ms) |
| Message Order | Per queue | Per partition | Per queue |
| Delivery | At-least-once, exactly-once | At-least-once, exactly-once | At-least-once |
| Retention | Until consumed | Time/size based | Until consumed |
| Protocol | AMQP | Kafka Protocol | JMS, AMQP, STOMP |
| Routing | Rich (exchanges) | Simple (topics) | Moderate |
| Replay | No | Yes | No |
| Clustering | Built-in | Native | Network of brokers |
Decision Guide
┌─────────────────────────────────────────────────────────────────┐
│ When to Use Which Broker? │
├─────────────────────────────────────────────────────────────────┤
│ │
│ Need complex routing? ──▶ RabbitMQ │
│ Need request/reply pattern? ──▶ RabbitMQ │
│ │
│ Need high throughput? ──▶ Kafka │
│ Need event replay? ──▶ Kafka │
│ Need stream processing? ──▶ Kafka │
│ │
│ Need JMS compliance? ──▶ ActiveMQ │
│ Need multi-protocol? ──▶ ActiveMQ │
│ Legacy Java integration? ──▶ ActiveMQ │
│ │
└─────────────────────────────────────────────────────────────────┘
Performance Characteristics
Throughput Comparison
Messages/second (approximate):
Kafka: ████████████████████████████████████ 1,000,000+
RabbitMQ: ██████ 50,000
ActiveMQ: ██████ 50,000
Latency Comparison
Average latency:
RabbitMQ: ██ 1-2ms
ActiveMQ: ███ 2-3ms
Kafka: █████ 5-10ms (but higher throughput)
Hybrid Architectures
Often, production systems use multiple brokers:
┌─────────────────────────────────────────────────────────────┐
│ Hybrid Architecture │
├─────────────────────────────────────────────────────────────┤
│ │
│ RabbitMQ ─────────▶ Request/Reply, Complex routing │
│ │ │
│ ▼ │
│ Kafka ──────────▶ Event streaming, Analytics │
│ │ │
│ ▼ │
│ ActiveMQ ─────────▶ Legacy JMS integration │
│ │
└─────────────────────────────────────────────────────────────┘
Sources
Arhitectura/rabbitmq vs kafka vs activemq.gif