본문 바로가기
Kafka

[Kafka] 카프카 토픽, 프로듀서, 컨슈머

by 개복취 2024. 3. 19.

 

 

토픽, 파티션 그리고 오프셋

토픽 :  특정한 스트림 데이터를 의미한다.

  • 데이터베이스 테이블과 유사함(제한사항 없는)
  • 토픽을 제한없이 설정할 수 있다.
  • 이름으로 구분되고 어떠한 메세지 포멧(binary, avro, parquet...)을 지원한다. 
  • 메세지의 순서를 데이터 스트림 이라고 한다.
  • 참고로, 토픽을 쿼리할 수 없다. 카프카 프로듀서를 통해 데이터를 보내고 컨슈머를 통해 데이터를 읽는 식으로는 가능하다.

 

파티션과 오프셋

  • 토픽은 파티션으로 분리된다. 각 파티션은 순서대로 정렬되어 나타난다.
  • 각 파티션에 있는 메세지는 서로다른 파티션마다 incremental id를 가진다. 이를 오프셋이라고 한다.
  • 데이터가 한번 파티션에 기록된다면 변경이 불가능하다.

토픽을 설정하고 데이터를 통해 다양한 서비스에 활용할 수 있다.

주의해야하는 사항

  • 파티션에 데이터가 기록이 된다면, 변경이 불가하다.
  • 데이터는 한정된 시간에만 존재 가능하다(기본설정은 한 주이고 설정할 수 있다.)
  • 오프셋은 같은 파티션에서만 의미있다. (파티션0의 오프셋3과 파티션1의 오프셋3은 서로 다른 데이터다.)
    • 오프셋은 앞의 데이터가 삭제되더라도 그 오프셋을 재사용할 수 없다.(같은 순번(오프셋)을 가질 수 없다라는 의미)
  • 파티션 내부에서만 순서가 보장된다. 파티션 끼리의 순서는 보장되지 않는다!
  • 데이터는 를 들고있다고 하더라도 파티션에 랜덤하게 들어간다.

 

 

프로듀서 + 메세지 키

 

프로듀서

  • 프로듀서는 데이터를 (파티션으로 구성되어있는)토픽에다가 쓴다.
  • 프로듀서는 어떤 파티션에 써야할지 미리 알고있다. 카프카 브로커(카프카 서버)가 그걸 갖게 된다.
  • 어디에 쓸지 정하는 주체는 카프카 브로커가 아니라 프로듀서이다. 또한, 카프카 서버에서 어떤 파티션이 고장났을 경우에 어떻게 복구할지 프로듀서가 알게 된다.
  • 프로듀서가 파티션으로 데이터를 보낼때 로드밸런서가 개입하게 된다. 프로듀서는 어떤 매커니즘에 따라서 모든 파티션에 걸쳐 데이터를 전송하기 때문이다.
  • 카프카의 한 토픽의 파티션은 다수의 프로듀서로부터 데이터를 받아올 수 있기 때문에, 스케일링을 사용하여 파티션을 관리한다.

 

프로듀서: 메세징 키

  • 프로듀서는 메세지 안에 메세지 키를 설정해줄 수 있다(키로 올 수 있는건 문자열, 숫자, 이진수, 등...).
  • 만약, key = null 이라면 데이터는 라운드 로빈 상태로 보내진다 (partitoin0 -> partition1 -> partition2 ... 이런식으로 로드밸런싱이 이뤄짐)
  • key != null이라면 해싱전략으로 인해 메세지는 같은 파티션에 들어갈 수 있다.

 

 

카프카 메세지 시리얼라이저

  • 프로듀서로부터 입력값에 대해 직렬로 된 바이트만을 받고, 출력값으로서 바이트를 컨슈머에게 전송한다.
  • 그러나, 메세지를 구성하고 보낼 때에는 바이트가 아니라서 시리얼화가 필수적이다. (객체와 데이터를 바이트로 변경)
  • 공통된 시리얼라이저를 카프카에 갖고있다. (String, JSON, Int, Float, Avro, Protobuf..)

 

카프카 메세지 해싱전략?

 

  • 키 해싱을 통해 카프카 파티셔너가 메세지를 받아서 전송할 파티션을 결정한다.
  • 디폴트 카프카 파티셔너로 murmur2 알고리즘을 사용하여 키 해싱한다. 
targetPartition = Math.abs(Utils.murmur2(keyBytes)) % (numPartitions - 1)

 

컨슈머 + 역직렬화

 

컨슈머

  • 컨슈머는 카프카 브로커, 서버에 데이터를 요청하고 되돌아오는 응답을 받는다. (pull model 이라고 함)
    즉, 데이터를 컨슈머에게 푸싱하는 건 카프카 브로커가 아니라 '풀 모델'이다.
  • 컨슈머는 어느 브로커로 부터 읽어야하는지 알기 때문에, 브로커가 죽더라도 컨슈머는 회복하는 방법을 알고 있다.
  • 데이터는 파티션 내부적으로는 오름차순으로 읽히는데, 2개이상의 파티션으로부터 읽어올 땐 순서가 보장되지 않기에 주의해야한다.

 

컨슈머 역직렬화

  • 프로듀서에 시리얼화 하는 과정이 있다면, 컨슈머에는 역-시리얼화(역직렬화)하는 과정이 존재한다.
  • 바이너리 형식으로 존재하는 데이터를 읽어서 객체로 변환해야 하는데, 컨슈머는 메세지의 형식이 무엇인지 미리 알고 있어야 한다.
  • 프로듀서가 전송하는 데이터 타입을 절대 변경해서는 안된다. 데이터 타입을 변경하고 싶다면 새로운 토픽을 만들어야 한다.

 

컨슈머 그룹 + 컨슈머 오프셋

  • 모든 컨슈머는 컨슈머 그룹단위로 데이터를 읽어낸다.
  • 같은 그룹에 속한 각각의 컨슈머는 각각 다른 파티션에서 읽게 될 것이다.

 

컨슈머가 더 많아진다면? / 하나의 토픽에 여러 컨슈머가 존재한다면? 

  • (좌) 하나의 컨슈머에서 하나의 파티션으로부터 읽는다. / 컨슈머4는 비활성(stand by)이 된다.
  • (우) 토픽에 대해 다수의 컨슈머 그룹이 있는 것은 괜찮다. 오직 하나의 컨슈머가 하나의 파티션에 지정된다.
  • 다수의 컨슈머 그룹이 필요한 이유? : 서비스당 하나의 컨슈머 그룹을 갖게 되므로 (group.id 로 그룹을 구분한다.)

 

컨슈머 오프셋

  • 카프카는 컨슈머가 어디까지 읽었는지 저장해 놓는다.
  • 데이터에 대한 처리를 컨슈머가 완료하면 컨슈머는 종종 오프셋을 커밋해야하고 카프카 브로커가 컨슈머 오프셋 토픽에 기록하라고 알린다. 오프셋을 커밋함으로써 어느만큼 성공적으로 읽었는지 카프카 브로커에게 알려줄 수 있게 된다.
  • 왜 이런 작업을 하는가? : 만일 컨슈머가 죽으면 다시 돌아와서 읽었던 곳에서부터 커밋한 컨슈머 오프셋으로 인해 다시 읽을 수 있다.

 

다양한 전달 의미론(Delivery semantics for consumers)

  • 가장 기본으로 자바 컨슈머는 자동적으로 오프셋을 기록한다(at least once)
  • 만약 수동으로 커밋을설정하게 된다면 3가지 옵션이 존재한다.
    1. At least once (가장 선호됨)
      • 메세지가 처리되고, 오프셋이 커밋된다.
      • 만약 처리도중에 오류가 생긴다면, 메세지는 다시 읽게 된다.
      • 이러한 처리로 인해 중복된 메세지의 처리가 생길 수 있다. 따라서, 시스템에 영향이 미치지 않았는지 (멱등한지(idempotent)) 확인해야하는 과정이 필수적이다.
    2. At most once
      • 메세지를 받자마자 오프셋이 커밋된다.
      • 만약 처리도중 오류가 발생한다면, 메세지는 손실될 수 있다. (그것들은 다시 읽히지 않을 수 있다.)
    3. Exactly Once
      • 카프카를대 카프카 워크플로우를 진행할 때(토픽에서 읽고 그 결과를 토픽에 다시 기록하게 될 때) 트랜잭션 api를 사용한다. 또는, 카프카에서 외부 시스템으로 간다면 멱등 컨슈머를 사용해야한다.

 

'Kafka' 카테고리의 다른 글

[Kafka] 브로커, 토픽, 주키퍼  (0) 2024.03.21