토픽이란?
하나의 데이터 스트림이라 볼 수 있음. 어떤 형태의 데이터도 가능하다.(json, binary, text 등). 토픽은 파티션으로 나뉜다. 각 토픽 파티션에는 오프셋이 증가하면서 데이터가 쌓인다. 데이터는 일정시간만 유지된다.(기본은 1주일). 파티션내에서만 순서가 보장된다. 오프셋은 데이터가 삭제된다하더라도 변경되지 않는다.
프로듀서가 메시지와 메시지의 키를 정의해서 토픽으로 메시지를 보낸다. 키가 없을 경우에는 라운드로빈으로 보낸다. 키가 존재하면 해싱해서 보낸다. 동일한 키는 항상 동일한 파티션으로 가게 된다.
메시지의 기본 구성 요소는 헤더, 키, 메시지, 압축방식, 파티션과 오프셋, 타임스탬프 로 구성된다. 메시지 직렬화를 통해 키와 밸류값을 바이너리 값으로 변경해준다.
카프카 파티셔너의 기본 해싱 알고리즘은 murmur2 algorithm이다. 파티션의 수가 바뀌면 바뀐다.
컨슈머
카프카의 컨슈머는 기본적으로 pull model이다. 카프카의 deserializer가 키와 밸류값을 역직렬화한다. 이 프로듀서에서의 직렬화와 컨슈머에서의 역직렬화의 타입을 토픽내에서는 변경하면 안된다.
모든 컨슈머는 컨슈머그룹으로써 데이터를 읽는다. 컨슈머가 파티션보다 많을 경우 파티션 개수만큼만 동작한다. 당연히 여러 컨슈머그룹이 하나의 토픽을 읽을 수 있다.
카프카에서는 __consumer_offsets라는 토픽에 컨슈머그룹이 어떤 offset까지 읽었는지 저장하고 있다. 오프셋을 커밋치면 다음것을 읽도록 한다. 이 커밋전략은 기본적으로는 자동인데 3개의 선택지가 존재한다.
1. At leat Once(usually preferred)
- 메시지가 처리된 후에 커밋, 오류가 날 경우 다시 읽도록 함.
- 이 경우 메시지를 다시 처리해도 동일한 결과가 나오도록 해야 함( idempotent )
2. At most once
- 메시지를 받자마자 커밋을 친다.
- 오류가 날 경우 메시지 손실이 날 수 있다.
3. Exactly Once
- 카프카 스트림즈 API를 활용하는 것처럼 완벽히 한번만 실행되도록 한다. - 아직 정확히 이해가 안된다.
autocommit을 Enable할 경우 Interval이 지난 후에 poll을 할 경우 commit을 하는 구조. 다음 Poll까지는 이번 배치의 Processing을 끝내줘야함.
offset을 가지고 있는 주기는 설정 가능하다. 2.0미만버전은 하루 리셋이 기본 그 이상은 7일이다.
컨슈머 설정 값
- heartbeat.interval.ms 몇초에 한번씩 보낼지
- session.timeout.ms : heartbeat가 몇초동안 오지 않으면 해당 컨슈머를 죽였다고 판정하고 리밸런싱할지
- Max.poll.interval.ms: 두 폴 사이의 Interval의 최대시간을 얼마로 기다려줄지, 이 시간이 지나면 컨슈머 죽었다고 판단
- max.poll.records : 한번에 몇개나 받을지
- fetch.min.bytes: 최소 몇 바이트까지 기다릴건지 fetch.max.wait.ms가 도달하면 어쨌든 가져온다.
- max.partition.fetch.bytes: 서버가 돌려주는 파티션별 maximum byte. fetch.max.bytes로 제한된다.
카프카 브로커
- ID로 정해지며 어떤 브로커로 연결되든지 모든 클러스터에 연결된다.
- Topic-A 3 파티션, Topic B 2 Partition이 있다고 가정하자.
- 카프카 브로커들은 bootstrap server로 불리기도 한다. 카프카 클라이언트가 하나의 브로커(부트스트랩 서버)에 연결하고 메타데이터를 요청하면 모든 브로커들의 리스트를 제공한다.
토픽 레플리케이션
- 두개 이상의 레플리케이션 팩터를 사용한다. 주로 2~3. 브로커가 오류가 생길경우 데이터를 서빙할 수 있게끔.
- 파티션의 리더 : 하나의 브로커가 하나의 파티션에 리더이다. 프로듀서는 파티션의 리더에게만 데이터를 서빙한다. 마찬가지로 컨슈머도 파티션의 리더에게서 데이터를 읽어간다. 하지만 2.4버전 이후부터는 컨슈머는 ISR중 하나에서 읽을 수 있도록 됨.
- 파티션 리더가 데이터를 받으면 다른 브로커가 그 데이터를 복사해간다. multiple ISR(In-Sync-Replica)
Producer Acknowledgements & Topic Durability
- acks = 0 : 프로듀서가 보내고 끝 (metric 모으는등의 곳에 쓰이면 쓰루풋에 좋음)
- acks = 1 : 프로듀서가 파티션 리더가 데이터를 썼는지 기다림 (리턴값을 기다림)
- acks = all : 프로듀서가 모든 레플리카에 다 동기화 됐는지 기다림 ( acks = -1 )
- Topic Durability : replica가 3이면 2개의 브로커 로스까지는 버틸 수 있음
- 딥하게 들어가면 min.insync.replicas 값이 있는데, 이건 최소 레플리카의 개수를 뜻한다. 최소 3개의 레플리카를 원한다면 이 값을 2로 해야한다. 흔한 설정값은 RF=3일 경우 min.insync.replicas를 2로 두는 것이다. 하나의 브로커가 down되어도 가능하도록.
Zookeeper
- 2.x 버전은 주키퍼가 필수, 3.x 버전은 kafka raft(kraft)를 사용(주키퍼 없이도 사용가능), 4.x 버전은 주키퍼 없이 사용
- zookeeper는 브로커들을 매니징하고, 파티션 리더를 뽑는 도움을 줬(었)다.
- 주키퍼 서버는 홀수로 동작하도록 되어있음.
- 4.0버전 전에 production-ready가 되려면 주키퍼를 사용해야함. 운영이 아니라면 주키퍼 없이 동작은 가능함.
- 카프카 클라이언트입장에서는 이젠 주키퍼에 연결할 일이 없음.
- 주키퍼는 카프카 클라이언트에서 연결됐을 경우 안전하지 않다.( Not secure )
- 주키퍼는 10만 파티션이상일 경우 스케일링 이슈가 있었음
- 주키퍼를 제거함으로써 더 쉽게 모니터링할 수 있고 하나의 시큐리티만 신경쓰면 되고( 클라이언트가 주키퍼를 연결하지 않으므로 ). 시작과 컨트롤러 셧다운 리커버리 타임이 짧다.
Sticky Partitioner
- key값이 존재하지 않고 produce를 빠르게 하고 있을 경우에 퍼포먼스 증가를 위하여 같은 파티션내에 넣어준다.
리밸런싱 과정
- 컨슈머그룹에 새로운 컨슈머가 들어오거나 나갔을 경우 어떤 파티션을 어떤 컨슈머가 컨슘할것인지 다시 결정하는 과정. 기본적으로는 Eager Rebalance로 Stop-the-world 이벤트가 발생하고 모든 파티션을 다시 재 지정해준다.
- Cooperative Rebalance는 비교적 최근 리밸런싱 과정인데 모두 멈추고 다시 하는것이 아니라 일부만 재 배정해주기 위해 멈춘다.
AutoCommit Interval
- 자바로 따지면 auto.commit.interval.ms = 5000일 경우 5초에 한번씩 커밋을 치는 것이라 보면 된다.
- 동작 과정은 한번의 poll에 3초씩 걸릴 경우, 1poll, 2poll 후에 6초가 지나게 되는데 이 후에 동작하는 것이라 보면 된다. (5초라고 해서 6초 이전에 갑자기 동작하는 것이 아니다) 어떤 poll 후에 동작할지?를 정하면 된다.
Producer Retries
- kafka 2.0 이하일경우 retry 0 이 default, 이상일경우에는 2147483647이라는 높은 값이 설정된다. 하지만, delivery.timeout.ms 라는 설정값보다 오래 retry 하지 않는다.
- retry.backoff.ms 설정값은 다음 리트라이를 몇 ms 이후에 할 것인지를 결정한다.
- 이 리트라이 설정은 key ordering에 의존할 경우 문제가 될 수 있는데 - (producer는 정상적으로 kafka에 데이터를 보냈으나, ack을 받는 도중에 네트워크 에러가 생겼다. 프로듀서는 다시 리트라이를 하게 돼서 중복되는 메시지를 보내게 된다.) 의 경우에 그렇다.
- 이런걸 방지할 수 있는게 Idemptoent Producer이다. Duplicate 메시지를 발견하고 두번 보내지 않도록 해준다. (3.0부터 default임)
Producer Message Compression
- snappy, ztsd 등의 압축을 적용하여 kafka에 보낼 수 있다.
- 장점 : 작아서 높은 throughput, kafka 용량 줄어듦
- 단점 : compress, decompress에 cpu 사이클이 들어감
- linger.ms batch.size를 조절하여 큰 배치를 진행하고 높은 처리량을 갖도록 한다.
- 브로커레벨에서의 compression도 가능하다.
- 프로듀서가 압축을해서 보낼경우 컨슈머에서 압축을 풀어줘야한다.(당연히)
Linger.ms & batch.size 등 프로듀서의 배치에 대해
- 기본적으로 카프카 프로듀서는 레코드를 asap 보내는데, 많은 메시지가 한번에 나갈 경우 배치 프로세스를 통해 보낸다.
- linger.ms 설정값은 보내기전에 얼마나 기다릴 것인지 결정한다. 이 값을 줄 경우 ex(5ms)를 줄경우 많은 메시지를 배치하게 된다.
- batch.size를 정하고 어떤 메시지가 이 이상일경우 바로 보낸다. 이 값은 최대치를 정하는 거다.
- 프로듀서가 높은 스루풋을 가질 경우 브로커가 속도를 못 따라가면 레코드는 메모리 버퍼안에 들어간다. 이 경우 async로 보내지 않고 블록을 하게 되는데 그 블록하는 시간이 max.block.ms이다.