아키텍처+MSA

[아키텍처/Kafka] 비동기 메시징 시스템: 개념 + Consumer Group을 통한 병렬 처리의 원리를 간단히 알아보자!

깃짱 2025. 9. 16. 22:00
반응형
반응형

🌏 비동기 메시징 시스템이란?

✅ 먼저, 비동기(Asynchronous)란?

  • 동기: 요청을 보내고 응답을 받을 때까지 기다림
  • 비동기: 요청을 보내고 응답을 기다리지 않고 다른 일 진행

메시징 시스템은 일단 메시지를 보내 두면, 상대가 나중에 처리해도 되도록 하는 구조입니다.

✅ 메시징 시스템이란?

서비스 간에 데이터를 직접 주고받는 대신, 중간에 메시지 브로커라는 우편함을 두는 방식입니다.

  • 발신자(Producer)는 메시지를 우편함에 넣고 바로 다른 일 하러 가는 역할
  • 수신자(Consumer)는 필요할 때 우편함을 열어 메시지를 읽어 처리하는 역할

이렇게 하면 만약에 수신자 서버가 제대로 동작하지 않는다 하더라도, 메세지 브로커에 할 일을 쌓아두었다가 추후에 서비스가 다시 정상 동작할 때 일을 처리할 수 있으므로 안정적으로 시스템을 운영할 수 있습니다.

✅ 비동기 메세징 시스템의 효능(?)

  1. 서비스 간 결합도 낮춤 = 느슨한 연결(Loosely Coupled)
    • A 서비스가 B 서비스에 직접 HTTP 요청을 보내면, B가 죽으면 A의 요청도 실패하면서 오류가 발생함. (서로 강하게 결합됨)
    • 메시징을 쓰면 A는 그냥 메시지를 큐에 넣고 끝이고, B가 살아날 때 처리 가능
  2. 안정성(장애 격리)
    • 한쪽이 터져도 전체 시스템이 같이 죽지 않음
    • 메시지가 중간에 안전하게 저장되기 때문에 복구 가능하고, 추후 처리 가능
  3. 확장성(Scalability)
    • 소비자(Consumer)를 여러 개 늘리면 동시에 메시지를 병렬 처리 가능하기 때문에 대량 트래픽 처리에 유리함
  4. 비동기 처리
    • 꼭 지금 처리할 필요 없는 작업(예: 이메일 발송, 로그 저장, 알림 전송)을 나중에 처리하기 때문에 사용자 응답 속도 빨라짐

✅ 사용 예시

주문 시스템을 구현한다고 했을 때,

  • 결제 성공 → 메시지 발행
  • 메일 서비스가 메시지를 보고 “영수증 발송”
  • 포인트 서비스가 메시지를 보고 “포인트 적립”
  • 재고 서비스가 메시지를 보고 “재고 차감”

이 모든 작업들을 다 메세징으로 구현하면 독립적으로 구현할 수 있습니다.

✅ 전통적인 메시징 시스템

  • RabbitMQ, ActiveMQ, IBM MQ
  • 원래부터 "메시지 큐(Message Queue)" 역할을 위해 만들어졌음
  • 특징: 메시지를 한 번 소비하면 사라짐 (Queue 성격)

✅ 카프카(Kafka)

  • 처음에는 LinkedIn이 "실시간 로그 수집/처리"용으로 만든 시스템
  • 메시징 시스템이면서 동시에 분산 로그 저장 시스템이라고도 불림.
  • RabbitMQ 같은 큐 방식과 다르게, 메시지를 지우지 않고 일정 기간 저장해 둡니다.

우리는 여러 가지 메세징 시스템이 있지만, 대용량 처리에 특화되고, 분산/확장성이 용이하며 메세지를 로그 파일로 디스크에 저장해 복구가 유연한 ‘카프카’에 대해서 공부해보려고 합니다.

먼저 용어부터 알아봅시당

🌏 카프카에서 사용되는 용어

이미지 출처:  https://medium.com/@cobch7/kafka-producer-and-consumer-f1f6390994fc

 

✅ Producer (생산자)

데이터를 보내는 쪽

예를 들어, 주문 서비스가 "주문 생성됨" 이벤트를 메시지로 발행하면 Producer 역할을 하게 됩니다. Producer는 데이터를 Topic이라는 곳에 써 넣습니다.

[Producer] → "주문 완료" → [Kafka Topic]

✅ Consumer (소비자)

데이터를 받아서 처리하는 쪽

메일 서비스가 "주문 생성됨" 메시지를 보고 영수증 메일을 보내는 경우, Consumer 역할을 하게 됩니다. Consumer는 Topic에서 메시지를 꺼내 읽습니다.

[Kafka Topic] → "주문 완료" → [Consumer]

✅ Broker (브로커)

카프카 서버 자체

한 대의 서버에 모든 메시지를 보관하면 위험하니까, 보통 여러 대의 Broker를 띄워서 클러스터를 만듭니다. 클러스터 안에서 토픽을 분산 저장하고, 메시지를 안전하게 보관합니다.

Kafka Cluster = Broker1 + Broker2 + Broker3 ...

✅ Zookeeper (또는 KRaft)

원래 카프카는 Zookeeper라는 별도 시스템을 통해 클러스터 상태를 관리했습니다.

  • 브로커 관리
    • 여러 대의 Kafka Broker가 있을 때, 누가 클러스터에 참여했는지 기록
    • 새로운 Broker가 들어오거나 나가면 상태 갱신
  • 리더 선출
    • 각 Partition에는 Leader Broker가 있고, 나머지는 Follower
    • 장애가 나면 Zookeeper가 새로운 Leader를 뽑음
  • 메타데이터 저장
    • 어떤 Topic이 있고, Partition이 몇 개인지
    • Consumer Group의 Offset 정보 등

하지만 Zookeeper를 별도로 운영하니 운영이 복잡해지고, 이중 관리 문제 + 성능의 병목이 되면서 KRaft Controller라는 프로세스 역할을 하는 Broker가 있는 KRaft 모드가 생겼습니다 

내장 합의 알고리즘(Raft) 도입으로, 최근 버전부터는 KRaft 모드로 Zookeeper 없이 동작 가능합니다.

✅ Topic

메시지를 넣어두는 큰 상자

모든 메시지는 반드시 특정 Topic에 저장됩니다.

  • 예: 주문 이벤트는 order-topic, 결제 이벤트는 payment-topic

✅ Partition

하나의 Topic을 여러 개 조각(Partition)으로 나눌 수 있습니다.

Partition을 여러 개로 나누면 메시지를 분산 저장하고 동시에 병렬 처리할 수 있습니다.

  • 예: order-topic을 3개의 Partition으로 나눔 → 주문 데이터가 나눠져 저장됨
order-topic
 ├─ Partition 0 : 주문1, 주문4, 주문7 ...
 ├─ Partition 1 : 주문2, 주문5, 주문8 ...
 └─ Partition 2 : 주문3, 주문6, 주문9 ...

각 Partition은 같은 Consumer Group 내에서는 딱 1개의 Consumer만 처리하도록 해서 중복 처리를 막습니당

✅ Offset

Partition 안에서 메시지의 고유한 번호

Consumer는 Offset을 기준으로 메시지를 읽고, “어디까지 읽었는지” 기록해 둡니다. Offset은 Consumer가 스스로 기억할 수도 있고, Kafka가 내부적으로 저장해 줄 수도 있습니다. (보통은 __consumer_offsets 토픽에 저장)

  • 예: Partition 0에서 Offset=5까지 읽었다면, 다음에는 Offset=6부터 읽음

✅ Consumer Group

Consumer들을 묶어서 하나의 그룹으로 만듭니다.

각 Partition은 한 그룹 안에서 단 한 Consumer만 처리합니다. 덕분에 메시지가 중복 처리되지 않고, 동시에 여러 Consumer가 나눠서 일을 할 수 있습니다.

 

 

(위 그림에서는 1:1이지만) 전체적인 카프카 구조에서 본다면, 토픽(Topic) ↔ 컨슈머 그룹(Consumer Group) 관계는  : 다 구조입니다.

 

1) 하나의 토픽을 여러 Consumer Group이 읽을 수 있음

예를 들어 order-topic에 「주문완료」 라는 메세지가 발행되면,

email service consumer group → 영수증 발송

point service consumer group → 포인트 적립

inventory service consumer group → 재고 차감

이런 식으로 같은 메세지가 각 그룹마다 한번씩 독립적으로 전달되도록 할 수 있습니다 = Pub/Sub 모델

 

2) 한 Consumer Group은 여러 토픽을 구독할 수도 있음

반대로, Consumer Group 하나가 order-topic, payment-topic 두 개 토픽을 동시에 구독할 수도 있습니다.

 

🌏 Consumer Group을 통한 병렬 처리

✅ 기본 아이디어

  • Consumer Group = 여러 개의 Consumer(소비자)를 하나의 그룹으로 묶은 것
  • Kafka는 한 Partition을 동시에 여러 Consumer가 읽을 수 없게 막아두는 대신 Partition 단위로 나눠서 Consumer들에게 배분

⇒ 병렬 처리 가능

✅ 예시 1: Topic에 Partition이 3개, Consumer Group 안에 Consumer 3명인 경우

order-topic (Partition 3개)
 ├─ Partition 0 → Consumer A
 ├─ Partition 1 → Consumer B
 └─ Partition 2 → Consumer C
  • 각 Consumer가 하나의 Partition을 맡아서 읽음 (효율 100%)
  • 동시에 3개의 메시지를 병렬로 처리 가능

✅ 예시 2: Topic에 Partition이 3개, Consumer Group 안에 Consumer 2명인 경우

order-topic (Partition 3개)
 ├─ Partition 0 → Consumer A
 ├─ Partition 1 → Consumer A
 └─ Partition 2 → Consumer B
  • Consumer A가 Partition 0,1 두 개 맡고
  • Consumer B가 Partition 2를 맡음

여전히 메시지들은 중복 없이 나눠서 처리되지만, 균등 분배는 아님

✅ 예시 3: Topic에 Partition이 3개, Consumer Group 안에 Consumer가 4명인 경우

order-topic (Partition 3개)
 ├─ Partition 0 → Consumer A
 ├─ Partition 1 → Consumer B
 └─ Partition 2 → Consumer C
Consumer D → 할당받을 Partition 없음 (Idle 상태)
  • Partition 수(3) < Consumer 수(4)면, 남는 Consumer는 놀고 있게 됩니다.

정리하자면,

  • Partition 수 ≥ Consumer 수일 때, 병렬 처리 극대화
  • Partition 수 < Consumer 수일 때 , 일부 Consumer는 할당 못 받고 놀게 됩니다.

✅ 예시 4: Topic 1개에 Consumer Group이 2개인 경우

똑같이 order-topic에 "주문 완료" 메시지가 들어온다고 했을 때, 이번에는 order-service-groupemail-service-group 두 개 그룹이 존재합니다.

두 그룹은 서로 독립적이므로 같은 메시지를 각각 한 번씩 소비하게 됩니다.

Producer → order-topic

Group 1: order-service-group
Partition 0 → Consumer A
Partition 1 → Consumer B

Group 2: email-service-group
Partition 0 → Consumer X
Partition 1 → Consumer Y

결과적으로 Pub/Sub 모델처럼, 같은 메시지가 두 그룹에 모두 전달됩니다.

  • order-service는 주문 DB 업데이트
  • email-service는 영수증 메일 발송

즉 한번의 publish로 두 번의 처리가 가능해집니다.

 

🌏 Kafka 효율적 활용 전략 (뽕 뽑기)

✅ 하나의 토픽을 여러 파티션으로 나누기

  • 이유: 파티션 수 ≥ Consumer 수일 때, 병렬 처리를 극대화할 수 있음
  • 예: Consumer Group에 Consumer 5개가 있으면, 최소 5개 이상의 Partition을 두어야 각자 일을 나눠 가질 수 있음
    • 만약에 Partition이 5개 미만이면 Consumer 하나는 놀게 되기 때문에 비효율적 
    • 파티션 = "작업 분산 단위"
  • 주의사항: 파티션을 너무 많이 만들면 관리 비용 증가 (메타데이터 관리, 리밸런스 비용 커짐).
    • 보통 트래픽량과 Consumer 수를 고려해서 적당한 파티션 수를 정합니다.

 

✅ 토픽을 기능별로 분리하기

  • 이유: 관심사가 다른 메시지를 한 토픽에 섞으면 Consumer가 불필요한 데이터까지 다 읽어야 함
    • order-topic → 주문 관련 이벤트
    • payment-topic → 결제 관련 이벤트
    • email-topic → 알림/메일 관련 이벤트

Consumer는 필요한 토픽만 구독해서 처리 효율 ↑

 

✅ Consumer Group이 여러 토픽을 구독하게 하는건 트레이드오프 고려하기

  • 하나의 Consumer가 여러 작업 (토픽 여러 개 구독)
    • 장점: 단일 서비스에서 여러 역할 가능해서 운영이 단순해진다 
    • 단점: 한 Consumer 장애 시 여러 기능이 동시에 멈출 수 있음
  • Consumer를 역할별로 분리 (각 서비스 전용 Consumer Group을 만드는 경우)
    • 장점: 독립적 확장/장애 격리 가능
    • 단점: 운영 복잡도 ↑ (브로커가 많이 필요해질 수 있음) 

🌏 참고자료

 

 

 

 

 

도움이 되었다면, 공감/댓글을 달아주면 깃짱에게 큰 힘이 됩니다!🌟
비밀댓글과 메일을 통해 오는 개인적인 질문은 받지 않고 있습니다. 꼭 공개댓글로 남겨주세요!

 

반응형