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
: 가장 마지막 오프셋부터 읽기