메시지 브로커(3) KAFKA

뜨뜨미지근한물
11 min readOct 9, 2022

--

kafka

이 글은 데브원영님의 [아파치 카프카 애플리케이션 프로그래밍] 강의를 듣고 정리한 내용입니다.

카프카는 아파치 재단에서 개발한 오픈소스 메시지 브로커 프로젝트로, 높은 처리량, 확장성, 영속성, 고가용성 등의 특징을 가진다.

이 중 SQS와 BULL에는 언급되지 않은 특징이 영속성 확장성인 것 같다.

  • 영속성: 메모리가 아닌 Disk 기반으로 데이터를 저장한다.
    FS와 페이지 캐시(디스크 데이터 캐싱)
  • 고가용성: 메시지 처리 프로세스를 클러스링하고, 데이터를 복제처리하며 메시징 작업을 진행한다.
  • 확장성: 카프카 클러스터(큐)에 붙는 클라이언트들은 물론이고, 실제 데이터가 담기는 큐(파티션)도 스케일 아웃이 가능하다.
  • 높은 처리량: 각 구성파트의 스케일 아웃으로 많은 트래픽을 받아낼 수 있다.
카프카 전체 구성도 | 출처) 아파치 카프카 애플리케이션 프로그래밍

카프카 관련 제품들을 간단히 살펴보면 위와 같은데, 정리하면 아래 5개로 나눠볼 수 있다.

  • 카프카
  • 카프카 프로듀서
  • 카프카 컨슈머
  • 카프카 커넥트
  • 카프카 스트림즈

참고한 강의는 5개의 제품들이 각각 어떤 의도를 가지고 있으며, 어떤 상황에서 어떻게 사용하는지를 상세히 설명하고 있다. 이번 글에서는 카프카, 카프카 프로듀서, 카프카 컨슈머에 대해서만 얕게 정리하고자 한다.

기본적으로 JVM 언어 (스칼라?!)로 작성된 언어이기에, 다른 언어로 작성된 클라이언트 제품을 공식적으로 지원하지 않지만, NodeJs 생태계에서는 kafkajs가 좋은 클라이언트인 것 같았다. kafkaJS통해 작성한 카프카 클라이언트 레벨 샘플 코드는 여기에서 살펴볼 수있다.

카프카 (클러스터)

브로커

카프카 클라이언트(프로듀서, 컨슈머..)와 데이터를 주고 받는 개별 서버(프로세스)로, 복수개의 브로커로 카프카 클러스터를 구성할 수 있다.

전에 공부했던 SQS, BULL과는 달리,
1. 하나의 비즈니스 로직으로 메시지를 처리하는 큐가 두개 이상일 수 있으며.
2. 소비된 메시지는 큐에서 사라지지 않고 보존된다.
라는 특징을 염두하고 각 부분들을 살펴보면 이해하기 편하다.

레코드

: 브로커로 전달되는 메시지 단위로, 아래의 요소들로 구성된다.

1. 메시지가 생성 or 브로커에 담긴 시간을 나타내는 timestamp,
2. 메타정보를 갖는 key/value 포맷의 header,
3. 메시지를 분류하는 (특정 파티션(물리적 큐)에 보낼때 활용) messageKey,
4. 실제 데이터가 담긴 message,
5. 파티션(큐)에 어느 순서에 저장되어 있는지를 가리키는 offset

토픽

: 메시지를 전달할 논리적인 큐의 구분 단위이다.

카프카 클라이언트는 토픽을 대상으로 메시지를 보내거나 구독할 수 있다. 토픽은 여러개의 파티션으로 구성될 수 있다.

파티션

: 토픽을 구성하는 실질적인 큐로써, 토픽 아래에서 독립적으로 메시지를 받아 저장한다.

하나의 토픽 아래 구성된 복수개의 파티션들은 리더 파티션과 팔로워 파티션으로 구분된다.
레코드의 인입과 소비처리는 리더 파티션에서만 담당하며.
팔로워 파티션은 리더 파티션으로부터 데이터를 복제해 유지한다.
이러한 파티션 그룹은 ISR*을 지향하며, 리더 파티션이 장애가 나면 팔로워 파티션이 리더 파티션으로 위임되면서 고가용성을 유지한다.

cf) ISR (In-Sync Replicas) 리더, 팔로워 파티션이 모두 싱크된 (레코드 및 오프셋 정보..) 상태를 지향하는 레플리케이션 그룹이다. ISR이 이뤄지지 않은 상태에서 장애가 나면 데이터가 유실될 수 있다.
( cf. config -unclean.leader.election.enable )

파티션은 추가 생성은 가능하지만, 제거는 불가능하다.

파티션 수의 변경시 컨슈머와의 맵핑을 조율하는 리밸런싱이 일어날 수 있다.

브로커

앞서 언급한 것처럼, 카프카 클러스터를 구성하는 서버(프로세스)로.

브로커와 파티션 구성 | 출처) 아파치 카프카 애플리케이션 프로그래밍
  1. 복수의 토픽과 토픽 하위의 파티션들을 가지고 있다.
    - 복수의 브로커라면, 복수의 파티션을 분산해서 가지고 있으며.
    - 브로커는 리더 파티션을 골고루 분배받아 파티션 hot-spot을 방지한다.
  2. 들어온 레코드를 FS로 저장하며, config에 따라 삭제하기도 한다.
  3. 컨슈머가 소비한 메시지들은 제거하지 않는대신 offset으로 관리한다.
    - 각 컨슈머(그룹)이 어느 레코드까지 데이터를 읽어서 소비했는지 기록
    - offset은 __consumer_offset 토픽에 저장
    (토픽은 이런 메타 데이터 저장소로도 쓰인다)
    - 컨슈머(그룹)이 장애 등으로 처리를 멈췄다 재개하면, offset부터 작업
  4. 카프카 클라이언트들은 리더 파티션 파악을 위해, 카프카 클러스터 연결시, 메타 데이터를 받아 리더 파티션의 위치를 파악해 놓는다.
    (실패시, LEADER_NOT_AVILABLE 익셉션 발생)
  5. 트래픽이 많다면, 브로커 (+내부 파티션) 을 스케일링 하여 대응할 수 있다.

카프카 프로듀서

카프카에 넣을 데이터를 만들고, 특정 토픽의 리더 파티션에 데이터를 전송한다.

프로듀서

: 해당 토픽의 리더 파티션을 가진 브로커와 통신해 레코드 전달

내부흐름

: 레코드 객체 생성 → Send() → Partitioner→ Accumulator → Sender → Broker

프로듀서 구성 | 출처) 아파치 카프카 애플리케이션 프로그래밍

파티셔너
- 브로커 내의 복수의 파티션에 메시지를 골고루 분산시키는 역할
- 레코드.메시지키 존재, 메시지키 해시값과 파티션 매칭해 레코드 전송
- 레코드.메시지키 존재X, 라운드로빈으로 레코드 분배 전송
- 커스텀 파티셔너 로직을 구현해 이를 조정할 수도 있다.

Accumulator
- 프로듀서 앱에서 생성된 레코드 버퍼를 묶어 배치로 전송시키는 역할

acks
- 프로듀서가 보낸 데이터가 브로커들에 정상적으로 저장되었는지 확인하는 옵션
- 0: 송신하고, 확인X
- 1: 송신 후, 리더 파티션 적재만 확인
- -1: 송신 후, 토픽의 모든 파티션에 적재 확인
- 데이터 전송 신뢰도와 성능(속도)은 tradeOff 관계.
~ acks와 replica insync 최소 개수를 맞춰 신뢰도와 성능을 조율하며.
이는 비즈니스 목적에 맞게 설정해야 한다!

카프카 컨슈머

특정 토픽의 리더 파티션으로부터 폴링한 데이터들을 소비(처리)한다.

컨슈머

: 해당 토픽의 리더 파티션을 가진 브로커와 통신해 레코드를 가져와 처리하고 파티션의 offset을 수정한다.

내부흐름

: Broker → Fetcher → Poll() → 레코드 객체 처리

컨슈머 구성 | 출처) 아파치 카프카 애플리케이션 프로그래밍

Fetcher
- 브로커로부터 레코드들을 미리 받아와 대기

커밋
- 컨슈머가 데이터를 어디까지 처리했는지 브로커에 전달
- auto commit: poll() 호출시 auto commit으로 컨슈머가 가져온 데이터의 최고 offset으로 __consumer_offset 갱신시킴 (config로 설정하면, 특정 시간대마다 자동 수행)
- 수동 commit: 컨슈머 로직에서 직접 commit 메서드 호출

파티션 오프셋과 커밋 | 출처) 아파치 카프카 애플리케이션 프로그래밍

단일 컨슈머
- 구독할 토픽을 연결하고, 토픽의 파티션들을 전부 할당받는다.

컨슈머 그룹
-
토픽의 파티션들의 메시지를 처리할 복수의 컨슈머로 구성된 그룹
- 컨슈머 그룹이 토픽에 대해 구독하는 형식으로, 브로커에 연결되면
그룹내 컨슈머가 파티션들을 할당받는다.
- 파티션은 하나의 컨슈머만 할당 받을 수 있어, 컨슈머 수 ≤ 파티션 수가 합리적인 구성이다!
- 한 토픽에 대해 복수의 컨슈머 그룹을 생성하면서 데이터에 대한 여러 프로세싱들을 둘 수 있다!

토픽과 컨슈머 그룹 맵핑 | 출처) 아파치 카프카 애플리케이션 프로그래밍

리밸런싱
- 컨슈머 그룹내 일부 컨슈머 장애시, 할당된 파티션을 정상 컨슈머에 재할당하는 과정
- 컨슈머가 추가되거나 제거되는 경우에도 발생한다.
- 모든 파티션과 커슈머를 훑어 재할당하므로, 컨슈머 많을수록 부하가 크다.
- 리밸런싱 자체가 장애와 비슷하다고 간주하고 대응해야 한다!

컨슈머 리밸런싱 | 출처) 아파치 카프카 애플리케이션 프로그래밍

컨슈머랙
- 파티션의 최고 offset과 특정 컨슈머 offset간의 차이
- 각 파티션~컨슈머그룹 마다 컨슈머랙이 생성된다.
- 컨슈머의 정상 동작을 모니터링하는 주요 지표! ( 0 ~ x 이하에서 증감 패턴)

정상적인 컨슈머랙 패턴 | 출처) 아파치 카프카 애플리케이션 프로그래밍

+ ETC

멱등성 처리

멱등성: 여러번의 연산을 수행해도 동일한 결과를 나타내는 것.

멱등성 프로듀서: 동일한 데이터를 여러번 전송해도 카프카 클러스터에 단 한번만 저장되게 하는 프로듀서

  • config: enable.idempotence
  • 기본 프로듀서는 적어도 한번 전달을 지원 (유실되진 않지만, 중복 가능)
  • 브로커에 레코드 전달시 Producer Unique Id와 Sequence Id (레코드 전달 ID)를 전달하고, 이후에 같은 PID, SID를 가진 레코드가 오면 이를 대조해 중복 적재를 방지한다.
  • PID 이슈와 적재 부하 등으로 멱등성 컨슈머를 비즈니스 로직적으로 설계하는게 나을 수 있다!

cf) 메시지 전달 QoS (서비스 품질) : 메시지 전달 보장 제어
- QoS 0 : 최대 한번 전달, 메시지가 최대 한번 전달되거나 전혀 전달 X
송신자는 수신 여부를확인하지 않음 ~ kafka producer acks = 0
- QoS 1: 메시지 최소 한번 전달, 중복 전달이 가능 (카프카 기반 메시징 방식)
송신자의 메시지 전달 확인 ~ kafka producer acks = 1 / -1
- QoS 2: 메시지 정확히 한번 전달, 중복 전달 X
신뢰도 높지만 가장 느린 전송 ~ kafka idempotence producer

트랜잭션 프로듀서

트랜잭션 ID로 여러 레코드들을 하나의 트랜잭션을 묶어, 1 or 0로 처리한다.

트랜잭션 프로듀서 | 출처) 아파치 카프카 애플리케이션 프로그래밍
  • 프로듀서: transaction.id를 설정해 같은 트랜잭션의 복수 레코드들을 송신한다.
  • 컨슈머: (isolation.level에 따라) 트랜잭션 레코드와 commit 레코드를 확인 후 데이터를 가져가 처리한다.

KafkaJs

: https://github.com/tulios/kafkajs
: https://kafka.js.org/

  • 공식 docs에서 설명하는 것처럼 다른 패키지 없이 순수 JS로만 이뤄져있다.
  • 클라이언트 레이어:
    - 어드민(브로커 레벨의 요청 맵핑 — 토픽 확인, 컨슈머 그룹 확인, 컨슈머랙 확인 ..), 컨슈머, 프로듀서, 클러스터 등이 Class로 구현되어 있다.
    - 각 클라이언트 클래스는 운영에 필요한 데이터들을 메모리 자료구조로 갖고 있다. ( ex. Offset 정보 Map() )
    - 이벤트 에미터를 붙여 카프카 클러스터의 상태 변경을 캐치 가능하다.
    (연결, 충돌, 리밸런싱, 요청 타임 아웃..)
  • 프로토콜 레이어:
    - protocols 하위 폴더 등 or %protocols% 파일에 kafka와 실제로 통신을 주고받는 코드들을 기능 및 명령어 단위로 작성해 두었다.
    - 상위 레이어에서 받아온 데이터를 인코딩해서 보내거나, 받은 데이터를 디코딩해서 보낸다.
    - 데이터 포맷과 인코딩 방식은 버전별로 관리된다. (클라이언트 레이어에서는 버전에 맞춰 protocol을 호출하는 듯하다.)
  • JVM 기반 kafka 클라이언트를 공부해보고, KafkaJs에서 지원해주는지 파악하면 좋을 듯 하다.
kafkaJs 패키지 폴더 구조
  • KafkaJs로 직접 작성한 샘플 코드는 여기 를 참고할 수 있다.

마치며

카프카는 CQRS, 마이크로 서비스 등 여러 아키텍처에서, 단순 메시지 브로커를 넘어 활용되고 있다. (이벤트 브로커)

이 외에도 데이터를 각 목적에 맞춰 구성해 공급하는, 데이터 파이프라이닝에도 활발히 사용되는 제품으로 틈틈이 공부해두면 좋을 것 같았다.

지금까지는 정말 기본적인 101만 훑어본 것으로 제대로 공부하려면 서두에 언급한 강의도 들어보길 추천한다.

--

--