正在加载,请稍候…

Message Queues: Kafka vs RabbitMQ - When to Use Which

Compare Kafka and RabbitMQ for different use cases. Learn messaging patterns, delivery guarantees, consumer groups, dead letter queues, and choosing the right tool.

Message Queues: Kafka vs RabbitMQ

Comparison Overview

Kafka:
  - Log-based, messages retained (days/forever)
  - High throughput (millions/sec)
  - Consumer groups replay from any offset
  - Great for event streaming, audit logs, analytics
  - Complex ops (Zookeeper/KRaft, partitions, topics)

RabbitMQ:
  - Traditional message queue (messages consumed then removed)
  - Complex routing (exchanges, bindings)
  - Built-in dead letter queues
  - Great for task queues, RPC, complex routing
  - Easier to operate

Kafka Patterns

import { Kafka, Partitioners } from 'kafkajs';

const kafka = new Kafka({ clientId: 'app', brokers: ['kafka:9092'] });

// Producer with guaranteed delivery
const producer = kafka.producer({
  createPartitioner: Partitioners.DefaultPartitioner,
});
await producer.connect();

async function publishEvent(topic: string, event: object, key?: string): Promise<void> {
  await producer.send({
    topic,
    messages: [{
      key: key || null,
      value: JSON.stringify(event),
      headers: {
        'event-type': topic,
        'timestamp': Date.now().toString(),
      },
    }],
  });
}

// Consumer group (multiple consumers = parallel processing)
const consumer = kafka.consumer({ groupId: 'order-processor' });
await consumer.connect();
await consumer.subscribe({ topics: ['orders', 'payments'], fromBeginning: false });

await consumer.run({
  eachBatch: async ({ batch, resolveOffset, heartbeat }) => {
    for (const message of batch.messages) {
      await processMessage(JSON.parse(message.value!.toString()));
      resolveOffset(message.offset);
      await heartbeat(); // Prevent rebalance timeout
    }
  },
});

RabbitMQ Patterns

import amqp from 'amqplib';

const connection = await amqp.connect(process.env.RABBITMQ_URL!);
const channel = await connection.createChannel();

// Work Queue (task distribution)
await channel.assertQueue('tasks', {
  durable: true,
  arguments: {
    'x-dead-letter-exchange': 'dlx',  // Failed messages go here
    'x-message-ttl': 3600000,         // 1 hour TTL
  }
});

// Publish task
channel.sendToQueue('tasks', Buffer.from(JSON.stringify({ type: 'email', userId: '123' })), {
  persistent: true,          // Survive broker restart
  messageId: generateId(),   // Idempotency
});

// Consume with ack
channel.prefetch(10); // Process max 10 unacked messages
channel.consume('tasks', async (msg) => {
  if (!msg) return;
  try {
    await processTask(JSON.parse(msg.content.toString()));
    channel.ack(msg);              // Success: remove from queue
  } catch (err) {
    const retries = (msg.properties.headers['x-retries'] || 0) + 1;
    if (retries < 3) {
      channel.nack(msg, false, false); // Dead letter for retry
    } else {
      channel.ack(msg);               // Give up: move to dead letter
    }
  }
});

// Dead Letter Queue: retry or investigate failed messages
await channel.assertExchange('dlx', 'direct');
await channel.assertQueue('dead-letters', { durable: true });
await channel.bindQueue('dead-letters', 'dlx', '');

Pub/Sub Patterns

// Kafka: naturally pub/sub (multiple consumer groups)
// Each consumer group gets ALL messages independently
kafka.consumer({ groupId: 'email-service' }).subscribe({ topic: 'user-events' });
kafka.consumer({ groupId: 'analytics-service' }).subscribe({ topic: 'user-events' });

// RabbitMQ: fanout exchange for pub/sub
await channel.assertExchange('events', 'fanout');
const q1 = await channel.assertQueue('email-notifications', { durable: true });
const q2 = await channel.assertQueue('analytics-tracking', { durable: true });
await channel.bindQueue(q1.queue, 'events', '');
await channel.bindQueue(q2.queue, 'events', '');

// Publish to all subscribers
channel.publish('events', '', Buffer.from(JSON.stringify(event)));

Choosing Between Kafka and RabbitMQ

Use Kafka when:
  - Event streaming (Kafka is the source of truth)
  - High throughput (>10k messages/sec)
  - Multiple consumers need same messages
  - Audit log or event replay needed
  - Building data pipelines

Use RabbitMQ when:
  - Task queues (workers pull jobs)
  - Complex routing (topic/header routing)
  - RPC (request-reply pattern)
  - Lower throughput but complex logic
  - Simpler operational requirements

For most microservices, RabbitMQ is simpler to operate; Kafka shines for data pipelines.