티스토리 뷰

*본 포스팅은 인프런 [데브원영]님의 아파치 카프카 프로그래밍 강의 세션 5를 수강하고 이를 참고해 작성했습니다.

 

[아파치 카프카 애플리케이션 프로그래밍] 개념부터 컨슈머, 프로듀서, 커넥트, 스트림즈까지! |

데브원영 DVWY | 실전 환경에서 사용하는 아파치 카프카 애플리케이션 프로그래밍 지식들을 모았습니다! 데이터 파이프라인을 구축하는데 핵심이 되는 아파치 카프카의 각종 기능들을 살펴보고

www.inflearn.com


카프카 프로듀서 개요

프로듀서는 카프카에서 데이터의 시작점이다. 프로듀서 애플리케이션은 카프카에 필요한 데이터를 선언하고 특정 토픽의 파티션에 전송한다. 프로듀서는 데이터를 전송할 때 리더 파이션을 가지고 있는 카프카 브로커와 직접 통신한다. 

- 리더 파티션: 프로듀서, 컨슈머와 직접 통신하는 역할
- 팔로워 파티션: 리더 파티션의 데이터를 복제해 두는 역할

 

프로듀서를 사용하는 방법은 크게 두 가지다. 우리가 저번에 실습했던 것처럼 'kafka console producer'를 사용해도 되고, 일반적으로는 프로듀서 애플리케이션을 필요한 기능에 맞게 직접 개발한다. 이때 직접 개발하는 경우 java, python, Go 언어를 사용할 수 있는데 오픈소스 아파치 카프카는 자바만 공식적으로 제공하므로 기능적으로 자바가 가장 안정적이다

 

프로듀서는 카프카 브로커로 데이터를 전송할 때 내부적으로 파티셔너, 배치 생성 단계를 거친다.

프로듀서의 구조와 함께 각 단계를 확인해 보자.

 

프로듀서의 내부 구조

다음 구조는 JAVA 기준 공식 라이브러리에서 기반한 구조로 다른 언어를 사용할 경우 다를 수 있다.

- ProducerRecord

프로듀서에서 생성해서 전송할 데이터를 정의하는 레코드. 이때 토픽에 저장되는 레코드와 달리 오프셋은 포함되지 않는 것을 기억해 두자. 오프셋은 클러스터에 저장될 때 생성되는 것이다. 여기서 필수값은 토픽, 메시지 값이다. 나머지는 선택사항.

 

-send()

레코드를 전송하는 메서드. 이때 send()를 하면 바로 데이터가 전송되는 것이 아니라 아래의 3단계를 거친 후에 클러스터에 전송된다.

 

- Partitioner (1단계)

어느 파티션으로 전송할지 지정하는 파티셔너로 메시지 키에 따라 다르게 동작한다. 기본값으로 DefaultPartitioner로 설정된다.

 

- Accumulator(2단계)

배치로 묶어서 전송할 데이터를 모으는 버퍼. TCP로 전송하기 때문에 한 번에 최대한 많이 보내는 것이 효율적이다. 버퍼에서 배치로 데이터를 모으다가 특정 시점에 한 번에 보내는 것이다.

 

 - Sender(3단계)

Sender 스레드는 Accumulator에 쌓인 배치 데이터를 가져가 카프카 브로커로 전송한다. 


프로듀서 파티셔너

프로듀서 API는 2개의 파티셔너를 제공한다: 'UniformStickyPartitioner' , 'RoundRobinPartitioner'

카프카 클라이언트 라이브러리 2.5.0 버전에서는 UniformStickyPartitioner가 기본 파티셔너로 설정되어 있다.

 

두 파티셔너의 동작 방법을 '메시지 키의 유무'에 따라 살펴보자.

 

[1] 메시지 키가 있는 경우 

UniformStickyPartitioner' , 'RoundRobinPartitioner'둘 다 메시지 키의 해시값과 파티션을 매칭하여 레코드 전송한다.

즉, 동일한 메시지 키가 존재하는 레코드는 동일한 파티션 번호에 전달된다.

만약 파티션의 개수가 변경될 경우(이때 줄이는 것은 안되니 늘리는 경우) 메시지 키와 파티션 번호의 매칭은 깨지게 된다

➡️ 그러므로 안정적인 사용을 위해서 파티션 개수는 충분히 큰 개수로 운영해야 한다. 

 

[2] 메시지 키가 없는 경우 

메시지 키가 없는 경우 두 파티셔너 모두 파티션에 최대한 동일하게 분배하는 로직이 들어있는데  'UniformStickyPartitioner'가 'RoundRobinPartitioner'의 단점을 개선했다.

 

-  RoundRobinPartitioner

PrducerRecord가 들어오는 대로 파티션을 순회하면서 전송한다. 이때 배치로 묶어 보내기 어려워서 Accumulator에서 묶이는 정도가 작기 때문에 전송 성능이 낮다.

 

- UniformStickyPartitioner 

Accumulator에서 레코드들이 배치로 묶일 때까지 기다렸다가 전송하므로 라운드로빈보다 향상된 성능을 가진다. 이때 배치로 묶일 뿐 결국 파티션을 순회하면서 보내기 때문에 모든 파티션에 분배되어 전송된다. 

 

+ 카프카 클라이언트 라이브러리는 사용자 커스텀 파티셔너를 생성할 수 있도록 Partitioner 인터페이스를 제공한다. 이를 상속받아 사용자 정의 클래스에서 메시지 키 또는 값에 따른 파티션 지정 로직을 적용할 수 있다. (사용자가 원한다면 메시지 키뿐만 아니라 값도 파티셔닝에 사용할 수 있다는 것! )


 프로듀서 주요 옵션

- 필수 옵션 : 디폴트 값이 없으므로 반드시 설정해야 하는 값

 bootstrap.servers 프로듀서가 데이터를 전송할 대상 카프카 클러스터에 속한 브로커와 호스트 이름으로 '어떤 카프카 클러스터로 보낼지 지정'한다. 2개 이상의 브로커를 입력해 일부 브로커에 이슈가 발생하더라도 접속하는 데에 이슈가 없도록 설정 가능하다. 
 key.serializer 레코드의 메시지 키를 직렬화하는 클래스를 지정한다.
 value.serializer 레코드의 메시지 값을 직렬화하는 클래스를 지정한다.

 

이때 직렬화란? 데이터 스토리지 문맥에서 데이터 구조오브젝트상태를 동일하거나 다른 컴퓨터 환경에 저장(이를테면 파일이나 메모리버퍼에서, 또는 네트워크 연결 링크 간 전송)하고 나중에 재구성할 수 있는 포맷으로 변환하는 과정

 

➡️ 프로듀서는 레코드를 보낼 때 직렬화를 설정해 전송하고, 컨슈머는 토픽에서 데이터를 가져갈 때 '역직렬화'를 해서 가져간다.

여기서 직렬화는 int, float, string 등 다양한 타입으로 가능한데 가장 일반적인 방법은 String으로 직렬화하는 방법이다. 

 

 

String 직렬화의 장단점

 

단점

- float, int를 String으로 직렬화할 경우 공간 낭비가 발생한다. 

 

장점

- kafka console consumer에서 데이터를 가져올 때 기본적으로 'Byte Array'로 역직렬화를 하는데, 이는 String으로 직렬화를 했을 때 디버깅, 데이터 확인이 가능하다는 점에서 유리하다. 

- 다양한 컨슈머 사용 시 String으로 통일하는 것이 운영상 이점이 크다.


- 선택 옵션 : 디폴트 값이 있다. 

선택 옵션 설명 기본값
acks 프로듀서가 전송한 데이터가 브로커들에 정상적으로 저장되었는지
전송 성공 여부를 확인하는데에 사용
1
(리더 파티션의 적재 성공 여부만 확인)
linger.ms accumulator에서 배치를 전송하기 전까지 기다리는 최소 시간
(즉시 전송 ➡️ 배치 모으려면 시간 늘리기 but 지연 발생 )
retries 브로커로부터 에러를 받고 난 뒤 재전송을 시도하는 횟수  2147483647
(굳이 여러 번 보낼 필요 없다면 낮추기)
max.in.flight.requests.per.connection 한번에 요청하는 최대 커넥션 개수
설정된 값만큼 동시에 전달 요청 수행
5
partitioner.class 레코드를 파티션에 전송할 때 적용하는 파티셔너 클래스 지정 2.5.0 버전은 UniformSticky 파티셔너
enable.idempotence 멱등성 프로듀서로 동작할지 여부 설정 2.5.0 버전에서 false
3 이상부터는 true (신뢰성 상승)
transactional.id 프로듀서가 레코드를 전송할 때 레코드를 트랜잭션 단위로 묶을지 여부  null

 

*멱등성: 동일한 연산을 여러 번 수행해도 결과가 달라지지 않는 성질


ISR(In-Sync-Replicas)와 ACKS 옵션

ISR(In-Sync-Replicas)

리더 파티션과 팔로워 파티션이 모두 싱크 된 상태. 즉 리더 파티션에 존재하는 오프셋이 팔로워 파티션에 존재하는 오프셋과 동일한 상태면 동기화가 완료된 ISR이라고 한다.

 

팔로워 파티션이 리더 파티션으로부터 데이터를 복제하는 데에는 시간이 걸린다. 리더 파티션에 새로운 레코드가 추가되어 오프셋이 증가하면, 팔로워 파티션이 위치한 브로커는 리더 파티션의 데이터를 복제한다. 리더 파티션에 데이터가 적재된 후 팔로워 파티션에 복제하는 시간차 때문에 리더 파티션과 팔로워 파티션 간에 오프셋 차이가 발생한다.


ACKS

"신뢰성"이냐 "성능"이냐 

카프카 프로듀서의 acks 옵션은 0, 1, all(-1) 값을 가질 수 있다. 이 옵션을 통해 프로듀서가 전송한 데이터가 카프카 클러스터에 얼마나 신뢰성 높게 저장할지 지정할 수 있다. 하지만 해당 옵션은 성능에도 영향을 주므로 카프카의 동작 방식을 상세히 이해하고 설정해야 한다.

 

복제 개수가 2(리더 1, 팔로워 1) 일 때 acks 값에 따른 동작 차이를 살펴보자.


ACKS = 0

ACKS가 0인 경우, 프로듀서가 리더 파티션으로 데이터를 전송했을 때 데이터가 저장됐는지 여부에 대한 응답 값을 받지 않는다. 리더 파티션은 데이터가 저장되면 해당 오프셋을 리턴하는데 이 값을 받지 않고 성공처리 하는 것이다. 

장점: 속도는 그 어떤 옵션값보다 빠르다. 

단점: 네트워크나 브로커의 장애로 인해 데이터 일부 유실이 발생해도 알 수 없다. 

➡️ 전체 흐름과 속도가 데이터 일부 유실보다 중요한 'GPS, 내비게이션' 서비스에 적합


ACKS = 1

acks를 1로 설정할 경우 프로듀서는 보낸 데이터가 리더 파티션에만 정상적으로 적재되었는지 확인한다. 만약 정상적으로 적재되지 않았다면, 성공할 때까지 재시도할 수 있다. 하지만 여전히 데이터가 유실될 수 있는 가능성이 존재한다. 

- 적재 여부를 응답받는 시간이 소요되므로 acks = 0일 때보다 성능이 낮다. 

- 리더 파티션 적재에 성공하더라도 팔로워 파티션에 동기화되지 않을 수 있는데, 팔로워에 복제하기 전에 리더에 장애가 발생하면 동기화되지 못해 데이터가 유실될 수 있다. 

- 하지만 실제로 사용해을 때 시간 지연이 크지 않고, 심각한 장애로 유실된 경우는 없다고 함.


ACKS = -1 (all)

acks = all 인 경우, 프로듀서는 보낸 데이터가 리더와 팔로워 파티션에 모두 정상적으로 적재되었는지 확인한다. 

 

- 리더 파티션 뿐만 아니라 팔로워 파티션까지 확인하므로 속도가 가장 느리다.

- 팔로워까지 데이터 적재를 확인하므로 일부 브로커에 장애가 발생하더라도 프로듀서는 안전하게 데이터를 전송하고 저장할 수 있음을 보장할 수 있다.

- min.insync.replicas 옵션값에 따라 데이터의 안정성이 달라진다. 

 

min.insync.replicas

프로듀서가 리더 파티션과 팔로워 파티션에 데이터가 적재되었는지 확인하기 위한 ISR 그룹의 파티션 개수

➡️ min.insync.replicas 값이 1이라면 ISR 중 최소 한 개 이상의 파티션에 데이터가 적재되었음을 확인하는 것!  = 리더 파티션만 확인하는 'acks = 1' 옵션과 같다. 그러므로 해당값을 2 이상으로 설정했을 때부터 acks = all의 의미가 있다. 

 

그럼 acks = -1이면 무조건 모든 팔로워를 확인해야 하는 걸까? 개수를 조절하는 방법이 없을까? ➡️ 있다.  min.insync.replicas 이용

 

만약, replication factor = 3이라면, 리더 1, 팔로워 2개로 구성된다.

여기서 acks = -1, min.insync.replicas = 2로 설정하면 리더 1, 팔로워 1 에게만 응답을 받고 나머지 팔로워 한 개는 확인하지 않음. 이를 사용해서 데이터 신뢰성과 성능 두 가지를 모두 확보할 수 있겠다. 

 

+ 마지막으로 실제 카프카 클러스터를 운영할 때 브로커가 동시에 2개가 중단되는 일은 극히 드물기 때문에 리더, 팔로워 각각 한 개 이상씩 데이터가 적재 완료되었다면 데이터는 유실되지 않는다고 볼 수 있다. 

공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
TAG
more
«   2024/09   »
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30
글 보관함