"아파치 카프카 애플리케이션 프로그래밍" 도서를 정리한 내용입니다.
4 카프카 상세 개념 설명
4.1 토픽과 파티션
토픽은 카프카의 시작과 끝이다. 카프카를 사용하는 것은 토픽을 만들면서 시작된다.
토픽에 대해 잘 이해하고 설정을 잘하는 것이 카프카를 통한 데이터 활용도를 높이는 길이다.
4.1.1 적정 파티션 개수
토픽 생성 시 파티션 개수 고려사항 3가지
- 데이터 처리량
- 메시지키 사용 여부
- 브로커, 컨슈머 영향도
데이터 처리 속도를 올리는 방법은 2가지다.
- 컨슈머의 처리량을 늘리는 것
- 서버의 사양을 올리는 스케일 업
- gc 튜닝
- 컨슈머를 추가해서 병렬처리량을 늘리는 것
- 데이터 처리량을 늘리는 가장 확실한 방법이다.
- 프로듀서가 보내는 데이터가 초당 1,000레코드이고 컨슈머가 처리할 수 있는 데이터가 초당 100레코드라면 최소한 필요한 파티션 개수는 10개이다.
- 만약 컨슈머 데이터 처리량이 프로듀서가 보내는 데이터보다 적다면 컨슈머 랙이 생기고, 데이터 처리 지연이 발생한다.
- 카프카 컨슈머를 개발할 때 내부 로직을 고민하여 시간 복잡도를 줄이기 위해 다양한 노력을 하는 것도 좋다.
파티션 개수를 무조건 늘리는 것만이 능사가 아니다. 파티션 개수를 늘리게 됨에 따라 컨슈머, 브로커의 부담이 있기 때문이다.
메시지 키를 사용함과 동시에 데이터 처리 순서를 지켜야 하는 경우에 대해 고려해야 한다. 메시지 키 사용 여부는 데이터 처리 순서와 밀접한 연관이 있다. 프로듀서가 기본 파티셔너를 사용하는 경우를 가정하자. 메시지 키를 사용하면 프로듀서가 토픽으로 데이터를 보낼 때 메시지 키를 해시 변환하여 메시지 키를 파티션에 매칭시킨다. 만약 파티션 개수가 달라지면 이미 매칭된 파티션과 메시지 키의 매칭이 깨지고 전혀 다른 파티션에 데이터가 할당된다.
4.1.2 토픽 정리 정책(cleanup.policy)
토픽의 데이터는 시간 또는 용량에 따라 삭제 규칙을 적용할 수 있다.
데이터를 오래동안 삭제하지 않으면 저장소 사용량이 지속적으로 늘어나게 된다.
cleanup.policy 옵션을 사용하여 데이터를 삭제할 수 있는 2가지 삭제 정책을 제공한다.
- delete(삭제): 데이터의 완전 삭제
- compact(압축): 동일 메시지 키의 가장 오래된 데이터를 삭제하는 것
토픽 삭제 정책(delete policy)
토픽을 운영하면 일반적으로 대부분의 토픽의 cleanup.policy를 delete로 설정한다. 토픽의 데이터를 삭제할 때는 세그먼트 단위로 삭제를 진행하다. 세그먼트는 토픽의 데이터를 저장하는 명시적인 파일 시스템 단위이다.
토픽 압축 정책(compact policy)
압축이란 메시지 키별로 해당 메시지 키의 레코드 중 오래된 데이터를 삭제하는 정책을 뜻한다. 메시지 키를 기준으로 오래된 데이터를 삭제하기 때문에 삭제 정책과 다르게 1개 파티션에서 오프셋의 증가가 일정하지 않을 수 있다. 즉, 1부터 10까지 오프셋이 있고, 4,5,6이 동일한 메시지 키를 가질 경우, 오프셋과 관계없이 중간에 있는 4번, 5번 오프셋의 레코드가 삭제될 수 있다는 뜻이다. 4,5,6이 동일한 메시지 키를 가지고 있는데, 6번에 비해 4번, 5번 오프셋의 레코드는 오래된 데이터이기 때문이다.
4.1.3 ISR(In-Sync-Replicas)
ISR은 리더 파티션과 팔로워 파티션이 모두 싱크가 된 상태를 뜻한다. 복제 개수가 2인 토픽을 가정해보자. 이 토픽에는 리더 파티션 1개와 팔로워 파티션이 1개가 존재할 것이다. 리더 파티션에 0부터 3의 오프셋이 있다고 가정할 때, 팔로워 파티션에 동기화가 완료되려면 0부터 3까지 오프셋이 존재해야 한다. 리더 파티션과 팔로워 파티션이 동기화된 상태에서는 리더 또는 팔로워 파티션이 위치하는 브로커에 장애가 발생하더라도 데이터를 안전하게 사용할 수 있다.
4.2 카프카 프로듀서
4.2.1 acks 옵션
카프카 프로듀서의 acks 옵션은 0, 1, all(또는 -1) 값을 가질 수 있다.
acks=0
acks를 0으로 설정하는 것은 프로듀서가 리더 파티션으로 데이터를 전송했을 때 리더 파티션으로 데이터가 저장되었는지 확인하지 않는다는 뜻이다. 리더 파티션은 데이터가 저장된 이후에 데이터가 몇 번째 오프셋에 저장되었는지 리턴하는데, acks가 0으로 설정되어 있다면 프로듀서는 리더 파티션에 데이터가 저장되었는지 여부에 대한 응답 값을 받지 않는다. 그렇기 때문에 프로듀서가 데이터를 보낸 이후에 이 데이터가 몇 번째 오프셋에 저장되었는지 확인할 수 없다.
acks=1
acks를 1로 설정할 경우 프로듀서는 보낸 데이터가 리더 파티션에만 정상적으로 적재되었는지 확인한다. 만약 리더 파티션에 정상적으로 적재되지 않았다면 리더 파티션에 적재될 때까지 재시도할 수 있다. 그러나 리더 파티션에 적재되었음을 보장하더라도 데이터는 유실될 수 있다. 왜냐하면 복제 개수를 2이상으로 운영할 경우 리더 파티션에 적재가 완료되어 있어도 팔로워 파티션에는 아직 데이터가 동기화되지 않을 수 있는데, 팔로워 파티션이 데이터를 복제하기 직전에 리더 파티션이 있는 브로커에 장애가 발생하면 동기화되지 못한 일부 데이터가 유실될 수 있기 때문이다.
acks=all 또는 acks=-1
acks를 all 또는 -1로 설정할 경우 프로듀서는 보낸 데이터가 리더 파티션과 팔로워 파티션에 모두 정상적으로 적재되었는지 확인한다. 리더 파티션뿐만 아니라 팔로워 파티션까지 데이터가 적재되었는지 확인하기 때문에 0또는 1 옵션보다도 속도가 느리다. 그럼에도 불구하고 팔로워 파티션에 데이터가 정상 적재되었는지 기다리기 때문에 일부 브로커에 장애가 발생하더라도 프로듀서는 안전하게 데이터를 전송하고 저장할 수 있음을 보장할 수 있다.
4.3 카프카 컨슈머
4.3.1 멀티 스레드 컨슈머
카프카는 처리량을 늘리기 위해 파티션과 컨슈머 개수를 늘려서 운영할 수 있다. 파티션을 여러 개로 운영하는 경우 데이터를 병렬처리하기 위해서 파티션 개수와 컨슈머 개수를 동일하게 맞추는 것이 가장 좋은 방법이다. 토픽의 파티션은 1개 이상으로 이루어져 있으며 1개의 파티션은 1개 컨슈머가 할당되어 데이터를 처리할 수 있다. 파티션 개수가 n개라면 동일 컨슈머 그룹으로 묶인 컨슈머 스레드를 최대 n개 운영할 수 있다. 그러므로 n개의 스레드를 가진 1개의 프로세스를 운영하거나 1개의 스레드를 가진 프로세서를 n개 운영하는 방법도 있다.
카프카 컨슈머 멀티 스레드 전략
하나의 파티션은 동일 컨슈머 중 최대 1대까지 할당된다. 그리고 하나의 컨슈머는 여러 파티션에 할당될 수 있다. 이런 특징을 가장 잘 살리는 방법은 1개의 애플리케이션에 구독하고자 하는 토픽의 파티션 개수만큼 컨슈머 스레드 개수를 늘려서 운영하는 것이다. 컨슈머 스레드를 늘려서 운영하면 각 스레드에 각 파티션이 할당되며, 파티션의 레코드들을 병렬처리할 수 있다.
4.3.2 컨슈머 랙
컨슈머 랙(LAG)은 토픽의 최신 오프셋(LOG-END-OFFSET)과 컨슈머 오프셋(CURRENT-OFFSET) 간의 차이다. 프로듀서는 계속해서 새로운 데이터를 파티션에 저장하고 컨슈머는 자신이 처리할 수 있는 만큼 데이터를 가져간다. 컨슈머 랙은 컨슈머가 정상 동작하는지 여부를 확인할 수 있기 때문에 컨슈머 애플리케이션을 운영한다면 필수적으로 모니터링해야 하는 지표이다.
카프카 명령어를 사용하여 컨슈머 랙 조회
kafka-consumer-groups.sh 명령어를 사용하면 컨슈머 랙을 포함한 특정 컨슈머 그룹의 상태를 확인할 수 있다.
$ bin/kafka-consumer-groups.sh --bootstrap-server localhost:9092 \
--group my-group --describe
컨슈머 metrics() 메서드를 사용하여 컨슈머 랙 조회
외부 모니터링 툴을 사용하여 컨슈머 랙 조회
버로우는 링크드인에서 개발하여 오픈소스로 공개한 컨슈머 랙 체크 툴로 REST API를 통해 컨슈머 그룹별로 컨슈머 랙을 확인할 수 있다.