3 minute read

카프카 톺아보기

카프카란?

카프카는 메시지큐 기반의 메시지 지향 미들웨어(Message Oriented Middleware, MOM)이다. 메시지큐는 운영체제 수업을 들었다면 공부했을 내용인데, 프로세스 간에 데이터를 서로 주고받는데 사용되는 방법 중 하나이다. 또 여기서 미들웨어란 중간에서 애플리케이션들을 연결해 이들이 서로 데이터를 교환할 수 있게 해주는 소프트웨어이다. 고로 MOM이란, 메시지를 통해 여러 분산되어 있는 시스템, 프로세스 간에 Connector 역할로 결합성을 낮추고, 이들이 서로 실시간 비동기식 데이터 교환을 할 수 있도록 도와주는 소프트웨어이다.

카프카 공식 홈페이지에서는 카프카를 High performance를 가지는 분산 이벤트 스트리핑 플랫폼이라고 정의하고 있고, 큰 회사부터 작은 회사까지 다양한 곳에 사용중이라고 설명하고 있다.

카프카의 기본 구조

image-20220902193625462

카프카는 크게 프로듀서, 브로커, 컨슈머로 구성된다.

브로커는 카프카의 서버라고 생각하면 된다. 브로커들이 데이터를 나눠서 저장하고 이중화 처리를 하기도 하고, 장애가 나면 장애복구를 하기도 한다.

주키퍼는 카프카 클러스터를 관리한다. 주키퍼 속에 카프카 클러스터에 대한 메타데이터(브로커id, 컨트롤러id 등)이 저장된다. 현재는 카프카를 띄우기 위해서는 주키퍼가 반드시 실행되어야 하지만, 주키퍼와 연동하지 않도록 메타데이터를 브로커 안에 저장하는 방식으로 카프카를 구성하는 작업이 진행중이다.

프로듀서는 카프카에 메시지를 넣는 역할을 하고, 컨슈머는 메시지를 꺼내는 역할을 한다.

쉽게 말해 카프카는 데이터를 A지점에서 B지점으로 이동시켜야할 때, 안전하게 또는 목적에 맞게 옮겨주는 역할을 하는 놈이라고 생각하면 된다.

image-20220902194345664

토픽은 메시지를 구분하는 단위로서, 파일시스템의 디렉토리와 유사하다. 우리가 파일을 분류할 때 여러 디렉토리를 만들어서 관리하는 것처럼, 뉴스용 토픽, 주문용 토픽 등 토픽도 주제에 맞게 메시지를 구분하여 저장하기 위해 존재한다.

한 개의 토픽은 한 개 이상의 파티션으로 구성이 되는데, 파티션은 메시지를 저장하는 물리적인 파일이다.

image-20220902194519637

image-20220902194708788

image-20220902194809864

위에서 보는 것과 같이 한 개의 파티션은 컨슈머 그룹의 한 개 컨슈머와만 연결이 가능한데, 이를 통해 컨슈머 그룹 기준으로 파티션의 메시지가 순서대로 처리되는 것이 보장된다.

카프카의 성능

카프카의 공식 홈페이지에서 카프카를 High performance platform으로 소개하고 있는 것처럼 카프카의 성능은 우수한 것으로 알려져 있다.

속도

카프카의 파티션 파일은 OS 페이지캐시를 사용한다. 파티션에 대한 파일 IO를 메모리에서 처리하고, Zero Copy를 지향한다. 보통 네트워크를 활용한 데이터 통신 시, 디스크 버퍼 -> 유저 레벨에서의 버퍼 -> 네트워크 버퍼의 식으로 데이터 복사가 존재하지만, 카프카는 디스크 버퍼에서 네트워크 버퍼로 직접 데이터를 복사하여 복사 횟수를 줄였다.

image-20220902195303094

위와 같이 묶어서 일감을 처리하면 I/O 횟수를 줄일 수 있고, 그만큼 인터럽트 횟수가 줄기 때문에 성능을 높일 수 있다.

고가용성

image-20220902195340041

위와 같은 구조이기 떄문에 수평확장에 유리하다

image-20220902195411497

출처 : https://www.youtube.com/watch?v=0Ssx7jJJADI

프로듀서

프로듀서의 기본 흐름

image-20220903124829812

카프카의 성능에서 언급했던 것처럼 배치로 메시지를 묶어서 모아서 브로커에 전송한다.

Sender의 기본 동작

image-20220903125047397

Sender는 별도의 스레드에 위치하여 버퍼의 메시지를 차례대로 꺼내 카프카 브로커에 전송한다.

처리량 관련 주요 속성

image-20220903125327336

배치가 다 차면 바로 전송이 일어나기 때문에, 배치 사이즈가 너무 작으면 전송횟수가 너무 많아져 카프카의 성능이 떨어지게 된다. 그래서 배치 사이즈를 적절하게 설정하는 것이 중요하다.

전송 결과 확인

프로듀서에서 전송 결과를 확인하거나 확인하지 않을 수 있다. 확인하는 방법은 크게 Future, Callback 두 가지가 있다.

Future

Future의 경우 RecordMetadata를 get() 메소드로 가져오는 과정에서 블로킹이 일어나기 때문에 배치 효과가 떨어지고 이는 처리량 저하를 가져온다. 따라서 처리량이 낮아도 되는 경우에만 사용하는 것이 좋다

Future<RecordMetadata> f = producer.send(new ProducerRecord<>("topic", "value"));
try {
    RecordMetadata meta = f.get(); // Blocking
} catch(ExecutionException ex){
    
}

Callback

Send 메소드에 Callback 객체를 전달하는 방식이다. Callback 객체는 전송이 완료되면 전송 결과를 onCompletion 메소드로 받게 된다. 이 때 Exception을 받게 되면 전송이 실패한 것이므로 이에 따른 예외처리를 해주면 된다. Blocking 방식이 아니기 때문에 배치가 쌓이지 않는다거나 하는 문제가 없어 처리량 저하가 없다.

producer.send(new ProducerRecord<>("simple", "value"),
              new Callback(){
                  @override
                  public void onCompletion(RecordMetadata metadata, Exception ex){
                      
                  }
              });

전송 보장과 ack

image-20220903130557457

에러 유형

  • 전송 과정에서 실패
    • 전송 타임아웃(일시적인 네트워크 오류 등)
    • 리더 다운에 의한 새 리더 선출 진행 중
    • 브로커 설정 메시지 크기 한도 초과
  • 전송 전에 실패
    • 직렬화 실패, 프로듀서 자체 요청 크기 제한 초과
    • 프로듀서 버퍼가 차서 기다린 시간이 최대 대기 시간 초과

컨슈머

컨슈머와 파티션의 관계

image-20220903131612254

파티션 갯수보다 컨슈머 그룹에 컨슈머가 많아지게 되면 컨슈머가 놀게된다. 컨슈머 그룹 하나당 파티션 하나만 할당할 수 있기 때문이다. 따라서 컨슈머 갯수를 늘리게 되면 파티션의 갯수도 늘려줘야 한다.

컨슈머 설정

  • 조회에 영향을 주는 주요 설정
    • fetch.min.bytes : 조회시 브로커가 전송할 최소 데이터 크기
      • 기본값 : 1
      • 이 값이 크면 대기 시간은 늘지만 처리량이 증가
    • fetch.max.wait.ms : 데이터가 최소 크기가 될 때까지 기다릴 시간
      • 기본값 : 500
      • 브로커가 리턴할 때까지 대기하는 시간으로 poll() 메서드의 대기 시간과 다름
    • max.partition.fetch.bytes : 파티션 당 서버가 리턴할 수 있는 최대 크기
      • 기본값 : 1048576 (1MB)

재처리와 순서

카프카 사용시 주의할 점은 컨슈머가 동일한 메시지를 다시 조회하는 가능성이 존재할 수 있다는 것이다.

  • 동일 메시지 조회 가능성
    • 일시적 커밋 실패, 리밸런스 등에 의해 발생
  • 컨슈머는 멱등성(idempotence)을 고려해야 함
    • 예 아래 메시지를 재처리할 경우
    • 조회수 1증가 -> 좋아요 1증가 -> 조회수 1증가
    • 중복 데이터를 단순 처리하면 조회수는 2가 아닌 4가 될 수 있음
  • 데이터 특성에 따라 타임스탬프, 일련 번호 등을 활용

세션 타임아웃, 하트비트, 최대 poll 간격

  • 컨슈머는 하트비트를 전송해서 연결 유지
    • 브로커는 일정 시간 컨슈머로부터 하트비트가 없으면 컨슈머를 그룹에서 빼고 리밸런스 진행
    • 관련 설정
      • session.timeout.ms : 세션 타임 아웃 시간(기본값 10초)
      • heartbeat.interval.ms : 하트비트 전송 주기(기본값 3초)
        • session.timeout.ms의 1/3이하 추천
  • max.poll.intervals.ms : poll() 메서드의 최대 호출 간격
    • 이 시간이 지나도록 poll()하지 않으면 컨슈머를 그룹엥서 빼고 리밸런스 진행

출처 : https://youtu.be/xqrIDHbGjOY