실전 아파치 카프카 정리

2022, Nov 07    
msa kafka

예제 소스

http://www.hanbit.co.kr/src/10280

소개

카프카를 처음 접하는 경우 읽으면 개략적으로 용도 및 활용 범위 등을 알 수 있는 입문서입니다.

summary

  • 카프타 전달 보증 수준
    • At Most Once : 메시지는 중복되지 않지만 상실될 수도 있다. 1회는 전달을 시도해본다. 재전송x
    • At Least Once : 메시지가 중복될 가능성은 있지만, 상실되지는 않는다. 적어도 1회는 전달한다. 재전송o
    • Exactly Once : 중복되거나 상실되지도 않고, 확실하게 메시지가 도달하지만, 성능이 나오기 힘들다. 1회만 전달한다. 재전송o
      • 프로듀서와 브로커의 상호 교환 사이를 살펴보면 양쪽 모두에게 시퀀스 번호를 관리해 중복되는 실행을 제거하는 방법을 사용한다 = 멱등성을 유지
  • 스키마 호환성
    • schema registry는 대수를 늘려도 확장 구성이 되지 않는다 2개 이상이면 충분
    • schema registry도 restapi 가 있다.
    • 후방(하위)호환성 - backward compatibility
      • 예전 스키마의 데이터를 새로운 스키마를 사용해서도 로딩할 수 있다는 성질
    • 전방(상위)호환성 - forward compatibility
      • 새로운 스키마의 데이터를 예전 스키마를 사용해서도 로드할 수 있는 성질
    • 완전 호환성 - full compatibility
      • 후방 호환성과 전방 호환성을 모두 갖춘 경우
  • 스트림의 처리 기본
    • 간헐적으로 유입되는 데이터를 수시로 처리하는 처리 모델이다

        while(running) {
        	ConsumerRecords<String, String> records = consumer.poll(1000);
        	for (ConsumerRecord<String, String> record : records) {
        		System.out.println(record.value());
        	}
        }
      
    • 스트림 처리 오류 다루기

      • 동일한 패턴에서의 예외처리가 여러 곳에 있는 경우, 그것을 대응하는 람다식 함수를 래핑해, 예외가 발생했을 경우 빈 목록을 반환하는 함수로 변환하는 메서드를 정의해서 사용한다.
        • stream에서 wrap메서드 호출을 통해 공통 처리

            // 호출
            stream.flatMapValues(wrap(v -> v.substring(3)))
                          
            // interface
            @FunctionalInterface
            private interface FunctionWithException<T, R, E extends Exception> {
            	R apply(T t) throws E;
            }
                          
            private static <V, VR, E extends Exception> ValueMapper<V, Iterable<VR>> wrap(FunctionWithException<V, VR, E> f) {
            	return new ValueMepper<V, Iterable<VR>>() {
            		public Iterable<VR> apply(V v) {
            			try {
            				return Arrays.asList(f.apply(v));
            			} catch(Exception e) {
            				e.printStackTrace();
            				return Arrays.asList();
            			}
            		}
            	}
            }
          
  • 컨슈머에서 파티션 할당
    • 컨슈머와 파티션의 매핑은 각 파티션에 하나의 컨슈머가 매핑된다. 반대로 파티션 수에 따라 하나의 컨슈머에 여러 파티션이 할당되는 경우가 있다.
    • 어사이너 assignor
      • 각 파티션을 컨슈머 그룹 내에 있는 특정 컨슈머에 맵핑할 것인가에 대해서 결정하는 로직
      • 종류
        • roundrobin : 매핑할 파티션을 컨슈머에 하나씩 차례대로 매핑한다.
        • range : 매핑할 파티션을 나열하고 컨슈머 수로 영역을 분할하여 할당한다.
        • sticky : 최대한 균형있게 할당하고 재할당 시에는 원래의 매핑에서 변경되지 않도록 할당한다.
      • KafkaConsumer를 생성할 때 옵션에서 설정할 수 있다

          partition.assignment.strategy
        
  • KSQL
    • 스트림과 테이블은 KSQL에서 다루는 구조화 데이터를 위한 것으로 스트림이 연속적으로 발생하는 구조화 데이터를 취급하는 반면, 테이블은 스트림이나 다른 테이블에 대해 집계한 데이터를 취급한다.
  • 파티션 수 설정시 고려사항
    • 카프카 클러스터의 메시지 송수신 측면
      • 브로커 수에 비해 파티션 수가 적으면 특정 브로커에만 리더 복제본이 존재하게 되어 부하가 몰리게 된다.(리더 복제본은 팔로워 복제본에 비해 부하가 올라가기 쉽다)
      • 브로커를 늘릴 때는 새롭게 증가하는 브로커에도 리더 복제본을 배치해서 각 브로커를 균등하게 처리하기 위해 더 많은 파티션이 필요하다.
    • 컨슈머 그룹의 할당
      • 파티션 수가 적어도 각 컨슈머 그룹에 속하는 컨슈머보다 많아야 메시지를 분산해서 수신할 수 있다.
    • 브로커가 이용하는 디스크
      • 카프카는 데이터를 되도록이면 순차적으로 기재해야 하기 때문에 되도록이면 브로커가 사용하는 디스크는 RAID, JBOD 등의 메커니즘을 통하지 않고 직접 사용하는 것이 바람직하다.
      • 전체 브로커에서 모든 디스크를 효율적으로 사용하기 위해서는 디스크 수 이상의 파티션 수가 카프카 클러스터 전체에서 필요하다
  • 복제본 수(replication-factor) 결정 시 고려사항
    • 복제본은 내장애성을 위한 구조로 되어있다. 그래서 replication-factor는 카프카 클러스터가 어느 정도의 장애에 견딜 수 있는가를 결정하는 중요한 설정이다.
    • 브로커의 장애 허용 대수와의 관계를 고려한다
      • min.insync.replicas의 설정
        • 프로듀서가 메시지를 보낼 때 송신처 파티션의 복제본 중 isr에 속하는 복제본이 최소 몇 개나 필요한지를 설정하는 브로커와 토픽의 구성이다.
        • (재배치 수) ≥ (min.insync.replicas) + (브로커의 장애 허용 대수)
          • 장애 허용 대수만큼의 브로커에 장애가 발생했을 때 서비스를 계속하기 위해서는 모든 파티션에서 ISR에 속하는 복제본의 수가 이 min.insync.replicas수 이상이어야 한다.
    • 토픽을 작성할 때 live broker 대수
      • (재배치 수) ≤ (브로커 총수) - (브로커의 장애 허용 대수)
      • 제대로 동작하고 있는 브로커(live broker)의 수가 재배치 수 이상 존재하지 않으면 토픽 작성에 실패한다.
  • 정리
    • kafka connect
      • 외부데이터를 카프카로 입력하고 카프카에서 외부로 데이터 출력 기능을 제공한다.
      • 카프카 커뮤니티에서 개발한 도구로 카프카 패키지에 포함되어있다.
      • confluent가 제시하는 디자인 패턴
        • kafka connect로 외부에서 데이터를 송신하고 → kafka streams를 이용해서 필요한 데이터를 처리하고 처리결과를 카프카에 송신하면 → kafka connect를 이용해서 연계 시스템에 데이터를 출력하는 방식
    • Fluentd
      • 오픈소스 데이터 수집 도구
      • 카프카에 특화된 것은 아니지만, 각각 입출력용의 플러그인이 제공된다.
      • 프로듀서쪽에서도 사용할 수 있고 컨슈머 쪽 도구로도 연결할 수 있다.
    • Kafka Rest Proxy
      • confluent가 오픈소스로 개발하고 있는 RESTFul API다.
      • 이걸 사용하면 http프로토콜로 각종 조작이 가능하다.
      • 카프카 클러스터와 별도로 데몬으로 띄운다.
      • 보안상 이유로 외부 시스템과의 방화벽 제한을 하고 싶다면, 이 서비스를 통해서 kafka와 통신할 수 있다.
    • Apache Flink or Spark
      • 스파크처럼 병렬 분산 스트림 처리가 가능하다
      • 이러한 미들웨어는 카프카에서 직접 데이터를 취득해서 스트림처리와 배치 처리를 모두 지원한다
      • Spark
        • oss의 병렬 분산 처리 프레임워크다
        • 맵리듀스보다 더 효율적이다
        • 데이터 처리 모델
          • 처리 대상 데이터를 RDD(Resilient Distributed Dataset)
    • Kafka Streams
      • 카프카가 빌트인으로 제공하는 스트림 처리를 위한 API다.
      • Stream DSL이라는 추상도가 높은 API 일부
      • 대표적으로 Apache Storm, Apache Flink, Spark Streaming

참고 코드

  • Kafka Connect
    • ByteArrayConverter

고민해볼 만한 부분

  • 링크드인은 엄격한 트랜잭션 관리는 다소 오버스펙이며, 높은 처리량 실현이 우선순위가 더 높았었다. 메시지 분실 없이 송수신 보증을 너무 중시하다보니 처리량이 나오지 않는 상황에서 카프카 출현했다고 하는데..
  • push하는 것보다, 수신시스템이 메시지를 pull하는 방식