Kafka와 RabbitMQ는 메시지 브로커(Message Broker) 로, 시스템 간 비동기 데이터 전송을 도와주는 역할을 하지만, 아키텍처와 목적이 다르다.
Microservices 간 이벤트 전달Topic, Direct, Fanout) 지원Task Queue): 이메일 전송, 알림 서비스트랜잭션 메시지가 많고, 메시지의 정확한 전달이 중요한 경우 RabbitMQ를
로그 기반의 대량 이벤트 처리가 필요한 경우 Kafka로 사용하는게 적절하다.
(1) 메시지 전달 방식
RabbitMQ는 메시지를 메모리에 저장하고 빠르게 전달하기 때문에 딜레이가 거의 없다.Kafka는 디스크에 로그를 저장하고 전달하기 때문에 조금 더 시간이 걸린다.(2) 메시지 라우팅
RabbitMQ는 "Direct Exchange" 방식 등을 사용하면 특정한 Consumer에게 즉시 메시지를 전달할 수 있다.Kafka는 메시지가 여러 Consumer 그룹으로 복제되면서 약간의 딜레이가 발생할 수 있다.(3) 트랜잭션
RabbitMQ는 트랜잭션 기능을 꺼두면 더욱 빠르게 메시지를 전달할 수 있다.Kafka는 메시지를 로그처럼 저장하여 데이터 보존성이 높지만, 그만큼 처리 속도는 조금 느려질 수 있다.
단일 진실 공급원(Single Source of Truth)
→ 이벤트 데이터를 장기간 저장하면, 시스템 전체에서 신뢰할 수 있는 데이터 원천으로 활용 가능.
재처리 / 리플레이 가능
→ 장애가 발생한 순간부터 다시 소비하거나, 이벤트를 처음부터 다시 처리할 수 있음.
스트림 데이터 처리에 적합
→ 실시간 데이터가 끊임없이 들어오는 환경에서 대규모 이벤트 스트림을 효율적으로 처리 가능.
┌─────────────┐
│ Producer │
└────┬────────┘
│
▼
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ Kafka Broker│◄────►│ Kafka Broker│◄────►│ Kafka Broker│ ← Kafka Cluster
└────┬────────┘ └────┬────────┘ └────┬────────┘
│ │ │
▼ ▼ ▼
Topic1 (Partition 0) Partition 1 Partition 2
▲
│
┌────┴────┐
│Consumer │ (개별 Consumer Group이 특정 Partition을 구독)
└─────────┘
Kafka에서는 메시지를 삭제하지 않고 로그 형태로 저장하므로, 특정 시점부터 다시 소비할 수 있다.
ex)
리플레이 처리 예시 (Java / Spring Kafka)
@Autowired
private KafkaConsumerFactory<String, String> consumerFactory;
public void replayFromBeginning(String topic, String groupId) {
Properties props = new Properties();
props.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, "localhost:9092");
props.put(ConsumerConfig.GROUP_ID_CONFIG, groupId);
props.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class);
props.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class);
props.put(ConsumerConfig.AUTO_OFFSET_RESET_CONFIG, "earliest"); // 처음부터 읽기
KafkaConsumer<String, String> consumer = new KafkaConsumer<>(props);
consumer.subscribe(Collections.singletonList(topic));
while (true) {
ConsumerRecords<String, String> records = consumer.poll(Duration.ofMillis(100));
for (ConsumerRecord<String, String> record : records) {
System.out.printf("리플레이 - topic: %s, value: %s%n", record.topic(), record.value());
}
}
}
earliest : 가장 처음부터 다시 읽기latest : 가장 마지막 오프셋부터 읽기