Kafka vs RabbitMQ & MSA환경에 kafka를 적용한다면? (with 예제코드)
대규모 시스템이란
- 방대한 양의 데이터를 처리하고 수 많은 사용자의 요청을 동시에 처리할 수 있는 시스템
- 이러한 시스템 설계는 확장성, 유지보수성, 성능, 안정성 등을 고려
- MSA 는 대규모 시스템 설계 아키텍쳐 패턴
- RabbitMQ,Kafka 같은
메시징 시스템
을 활용하여 각 서비스 간의 효율적인 데이터 통신과 확장성을 보장
메세징 시스템?
- Queue 형태로 메세지를 저장. 송신자 (Producer)가 메세지를 큐에 넣으면, 수신자(Consumer)는 자신의 속도에 맞춰 Queue에서 메세지를 처리.
- Message Broker를 사이에 두고, 송신자(Producer)와 수신자(Consumer)가 간접적으로 데이터를 주고 받음
- MSA 환경에 적용한다면
- 송신자 (Producer): MSA에서 메세지를 발행하는 곳 (e.g. 주문상태 변경에 대한 메세지를 order-msa에서 발행)
- 수신자 (Consumer): MSA에서 메세지를 처리하는 곳 (e.g. 재고차감을 하기 위해 stock-msa에서 주문상태 변경 메세지를 수신)
기능
비동기 처리
Producer 메세지를 발행한 후, consumer가 메세지를 처리할 때까지 기다리지 않아도 됨 → 시스템의 유연성, 처리 효율성 ⬆️
데이터 손실 방지
데이터를 메세지 큐에 안전하게 보관. consumer가 메세지를 놓치지 않고 처리 가능 → 데이터 손실 방지, 신뢰성 있는 통신 보장
부하 분산
여러 consumer가 큐의 메세지를 가져가서 처리 → 시스템의 부하를 분산. 성능 ⬆️ 대용량 데이터를 처리할 때 유용
스케일링
메세지 큐 시스템은 수평적 확장이 용이. 더 많은 Producer, Consumer를 추가 가능 → 처리 능력 ⬆️
RabbitMQ vs Kafka
둘다 메세징 시스템이지만 어떤 점이 다를까?
차이점 | RabbitMQ | Kafka |
Producer ↔ Consumer의 상호작용 방식 | Producer는 메세지를 보내고 메세지가 의도한 Consumer에 도착했는지 모니터링 → 우편물을 받아서, 수취인에게 배달하는 우체국 | Producer는 Consumer가 메세지를 검색했는지에 대한 여부와 상관없이 Queue에 저장 → 다양한 장르의 메세지를 책장에 배치하는 도서관. 도서관 회원 (Consumer)가 책의 내용을 기억 |
아키텍쳐 | - 복잡한 메시지 라우팅을 위해 설계 - 메세지큐 기반 설계 - push 모델 사용 |
- 더 복잡한 아키텍처를 사용. 처리량이 높은 스트림 이벤트를 관리 - 파디션 기반 설계 - pull 모델 사용 |
메세징 처리 방식 | - 메시지 전달의 우선순위를 지정하는 범용 메세지 브로커 - 우선순위 대기열 지원 - 순서대로 메세지 처리 - Consumer가 메세지를 처리 → 브로커에 확인 (ACK) 응답 전송 → 브로커가 대기열에서 메시지 삭제 - 지연시간을 줄이고, 복잡한 메세지를 분산 |
- 우선순위 대기열 지원 X - 토픽과 파티션을 사용하여 메세지 처리. 파티션 내부에서의 순서 보장 (offset) - 메세지를 로그 파일에 추가. 보존기간이 만료될 때까지 보관. 로그파일에 있는 메세지는 언제든 데이터 처리 가능. |
성능 | - 초당 수천 개 메시지 처리 (그 이상은 다중 브로커 설정 필요) - 지연시간이 짧다 |
- 초당 수백만 개의 메시지 처리 - 대용량 데이터를 처리한다 |
- 어떤게 더 좋고 나쁜 것은 없다!
- 구축하는 시스템의 요구사항에 더 적합한 것을 선택하는 것이 좋다.
왜 MSA에서 쓰일까?
서비스간 비동기 통신
- 서로 다른 서비스 간의 동기적인 호출이 많아지면 시스템 성능, 안정성에 영향
- Kafka, RabbitMQ → 비동기 메시징을 통해 MSA간 통신을 비동기적으로 처리
확장성
- MSA 환경에서는 증가하는 트래픽이나 데이터 양에 대응하여 성능을 유지시키거나 향상 시킬 수 있는 환경이 중요 합니다
- Kafka → 파티셔닝 (수평적 확장), 클러스터에 브로커 추가 (다운타임 없이 추가 가능)
- RabbitMQ → 클러스터링, 분산 큐
데이터 일관성, 신뢰성
- 각 MSA는 독립적으로 데이터를 관리 (e.g. msa 별로 분리된 database 사용) → 서비스 간 데이터 일관성 유지 중요
- Kafka → 로그, 메세지 저장 기능으로 데이터 일관성 유지, 메세지를 여러번 읽을 수 있도록 처리 가능
- RabbitMQ → 메세지 디스크 저장, 다른 큐에 복제 하여 데이터 내구성 유지, 실패한 메세지 재처리 지원
서비스 간 결합도 ⬇️
- Kafka → 이벤트 기반 아키텍처 지원. 서비스 간 독립적으로 동작 가능.
- RabbitMQ → 큐 기반 메세징을 통해 서비스 간 메세지 전달.
✅ 요약 → RabbitMQ, Kafka는 MSA 에서 각각의 서비스가 독립적으로 동작하면서도 효율적으로 통신할 수록 지원하는 도구!
Kafka 를 MSA에 적용한다면
이벤트 중심 아키텍처 (Event-Driven Architecture)
- 각 MSA간 통신을 이벤트 기반으로 처리. 통신을 담당하는 주체는 Kafka
- 예시) 주문생성, 결제완료, 배송준비 등의 이벤트를 Kafka에 전달, 이를 구독하는 다른 서비스가 해당 이벤트를 처리하여 필요한 작업 수행
실시간 데이터 처리
- 실시간으로 발생하는 대규모 데이터를 스트리밍 방식으로 처리
- 예시) 사용자 행동로그, 모니터링 데이터를 실시간 분석 → 다른 서비스 전달
모니터링과 로깅
- 중앙 집중식 로그 수집
이벤트 중심 아키텍처 Event-Driven Architecture 란
핵심 개념
이벤트
시스템 상태변화나 중요한 작업을 나타내는 객체. (e.g. OrderCreated, DeliveryCompleted)
이벤트 발행
MSA서비스에서 이벤트가 발생했을 때 (주문 생성 요청이 들어왔을 때), 해당 이벤트를 Kafka와 같은 메시징 시스템에 전달
이벤트 구독
- 특정 이벤트에 관심 있는 MSA들이 해당 이벤트를 구독.
- payment-msa는 order-msa가 발행한 OrderCreated 이벤트를 구독하고, 결제 처리 시작
비동기 처리
- 이벤트를 발생한 MSA 서비스와 처리하는 MSA서비스가 비동기적으로 동작
- order-msa는 OrderCreated 이벤트를 발행 후, 다른 MSA들이 처리하는 것을 기다리지 않고 (동기 X), 후에 필요한 비즈니스 로직 수행
코드 예시
build.gradle
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.springframework.cloud:spring-cloud-starter-netflix-eureka-client'
implementation 'org.springframework.kafka:spring-kafka'
implementation 'org.springframework.boot:spring-boot-starter-actuator'
compileOnly 'org.projectlombok:lombok'
runtimeOnly 'com.h2database:h2'
annotationProcessor 'org.projectlombok:lombok'
...
}
application.yml
spring:
application:
name: order-msa
datasource:
url: jdbc:h2:mem:testdb
driverClassName: org.h2.Driver
username: sa
password:
h2:
console:
enabled: true
jpa:
hibernate:
ddl-auto: update
show-sql: true
kafka:
bootstrap-servers: localhost:9092
consumer: # TODO kafka consumer, producer에 대한 설정값 수정은 여기에서
group-id: ${spring.application.name}-group
auto-offset-reset: earliest
key-deserializer: org.apache.kafka.common.serialization.StringDeserializer
value-deserializer: org.apache.kafka.common.serialization.StringDeserializer
properties:
spring.json.trusted.packages: '*'
producer:
key-serializer: org.apache.kafka.common.serialization.StringSerializer
value-serializer: org.apache.kafka.common.serialization.StringDeserializer
eureka:
client:
service-url:
defaultZone: http://localhost:8761/eureka/
server:
port: 8083
이벤트 발신
- KafkaTemplate을 사용해서 이벤트를 발신.
- 토픽과 보내고자 하는 이벤트 내용을 담아서 전송.
- e.g. 배송시작 시 → DeliveryCreatedEvent를 생성해서 Notification-Msa가 관련 알림을 보낼수 있게 처리
@Service
@RequiredArgsConstructor
public class DeliveryService {
private final DeliveryRepository deliveryRepository;
private final KafkaTemplate<String, Object> kafkaTemplate; // 자동 주입
@Transactional
public void createDelivery(...) {
Delivery delivery = new Delivery(...);
deliveryRepository.save(delivery);
DeliveryCreatedEvent event = new DeliveryCreatedEvent(...); //이벤트에 필요한 값을 넣어주세요
kafkaTemplate.send("delivery-created", EventSerializer.serialize(event));
}
}
이벤트 수신
- @KafkaListener 를 사용하여 메세지를 수신
- groupId는 application.yml에서 설정한 consumer group id
@Transactional
@KafkaListener(topics = "delivery-created", groupId = "notification-group")
public void handleDeliveryCreatedEvent(String message) {
DeliveryCreatedEvent event = EventSerializer.deserialize(message, DeliveryCreatedEvent.class);
// 이벤트로 해야하는 후 처리 진행 (비즈니스 로직)
// e.g. 배달완료 이벤트 발행
}
Event-Driven + Kafka 구현 예시
간단한 이벤트 중심 아키텍처를 맛(?) 볼수 있는 예제 코드 여기서 확인
예시 코드 구조
기술스택
- Spring-Kafka
- Kafka, Kafka-UI, Zookeeper, Zipkin
- Spring Cloud-Discovery Client, Server, Gateway
- Spring JPA
- H2
이벤트 흐름
코드베이스에 포함된 것:
- docker-compose.yml (zookeeper, kafka, kafka-ui, zipkin)
- 실행이 안된다면, 환경변수를 설정해주세요!
DOCKER_DEFAULT_PLATFORM=linux/amd64
- 실행이 안된다면, 환경변수를 설정해주세요!
- 주문 생성, 주문 목록 조회 http 테스트 파일
- order-msa, payment-msa, notification-msa skeleton 코드, gateway, eureka
- 객체 Serialize, De-Serialize 유틸클래스
코드가 제대로 실행되고 있는지 확인 하는 방법
✅ docker-compose 실행 후 컨테이너 확인
✅ 모든 msa가 정상적으로 실행되는지 확인 → Eureka 대시보드 (http://localhost:8761)
✅ Kafka UI 에서 topic, consumer가 정상 등록되어있는지 확인
✅ api-test.http 파일을 통해 주문생성 API 실행. 각 Topic에서 메세지가 제대로 들어오는지 확인
- 주문 → 결제완료 메세지
- 결제 → 주문생성 메세지
- 알림 → 주문완료
✅ notification-msa에서 주문완료 된 로그 메세지 출력 확인
더 알아보고 싶다면 🤓
이벤트 중심 아키텍처를 적용했을 때 msa에서 transaction 관리는 어떻게 하지?
- Saga Pattern
- Outbox Pattern 등등
- 참고 자료
그 외, 읽어보면 좋은 글