Streams
- https://www.confluent.io/blog/introducing-kafka-streams-stream-processing-made-simple/
- https://kafka.apache.org/documentation/streams
OVERVIEW
- 카프카에 저장 된 데이터 처리와 분석을 위한 하나의 클라이언트 라이브러리로 결과를 카프카로 다시 쓰거나 최종 출력 값을 하나의 외부 시스템으로 보낼 수 있음
- 이벤트시간과 처리시간을 적절히 구별, windowing 지원, 단순하나 효율적인 어플리케이셔 상태 관리 등의 스트림 처리 개념
- 진입 장벽이 낮음
- 단일 머신에서 작은 규모의 스케일로 poc 등을 빠르게 작성하여 실행 할 수 있으며, 스케일 업이 필요한 경우 다수의 머신 위에 추가로 어플리케이션 인스턴스를 실행하기만 하면 됨
- 카프카의 병렬처리 모델을 활용한 직관적인 로드 밸런싱
- 몇 가지 하이라이트
- simple and lightweight client library
- no external dependencies on systems other than Apache Kafka itself
- fault-tolerant local state
- one-record-at-a-time processing, event-time based windowing operations
- high-level Stream DSL, low-level Processor API
CORE CONCEPTS
Streams Processing Topology
하나의 스트림은 카프카 스트림에서 제공하는 가장 중요한 주상화 이며, 경계 없는, 또한 지속적으로 업데이트 되는 데이터 셋을 나타냄
- 스트림은 순서화, 재생성, 장애 내구성을 갖는 불변의 데이터 레코드 시퀀스이며, 여기에 하나의 데이터 레코드는 키/값 쌍으로 정의 됨
토포로지 내 두 가지 특별한 프로세서가 있음
소스 프로세서: 하나의 소스 프로세서는 어떤 상위 스트림도 갖지 않는 특별한 종류의 스트림 프로세서를 말하며, 해당 토폴로지 내 하나의 입력 스트림을 생성한다 (하나 이상의 카프카 토픽들로부터 레코드를 소비하여) 또한 생성한 것을 하위스트림 프로세서로 전달한다
싱크 프로세서: 하나의 싱크 프로세서는 어떤 하위 스트림도 갖지 않는 특별한 종류의 스트림 프로세서를 말하며, 해당 토폴로지 내 상위 스트림으로부터 받은 레코드를 특정 카프카 토픽으로 보낸다
그림 1 (processor topology)
카프카 스트림은 스트림 처리 토포로지를 정의하기 위해 두 가지 방법을 제공한다
Kafka Streams DSL: 가장 통상적인 데이터 변환 연산자들을 제공 (map, filter, join, aggregations 기능)
lower-level Processor API: 개발자에게 사용자 프로세서를 정의하고 연결, 상태 저장소와 상호 작용하도록 허용한다
하나의 프로세서 토폴로지는 단지 스트림 처리 코드를 위한 하나의 논리적인 추상화이며, 논리적 토폴로지는 런타임시에 병렬 처리를 위해 어플리케이션 안에 인스턴스화, 복제 되어진다
Time
스트리밍 처리에서 중요한 측면 중에 하나는 시간 개념이다 (이것을 어떻게 모델링하고, 통합할 것인지)
스트림 내 통상적인 시간 개념들
event time
processing time
ingestion time
카프카 설정을 통해 event time 과 ingestion time 간의 선택이 가능하다 (0.10.x 버전부터는 타임스탬프가 카프카 메시지에 자동으로 내장 되며, 카프카 설정을 통해 타임스탬프를 event time 또는 ingestion time 으로 표현 가능)
- 카프카 설정은 브로커 레벨이나 토픽 단위로 설정 지정이 가능하다
TimestampExtractor 인터페이스
어떤 입력 레코드를 처리하여 새로운 출력 레코드를 생성 - process() 내 context.forward() 함수 호출: 입력 레코드 타임스탬프를 상속받아 출력 레코드로 바로 적용
간헐적 함수를 통해 새로운 출력 레코드를 생성 - punctuate(): 스트림 테스크의 현재 내부 시간으로 출력 레코드 시간을 적용 (context.timestamp() 로 획득)
집계에 경우, 갱신 레코드의 집계 결과의 타임스템프는 업데이트 시 가장 최근에 도착한 입력 레코드를 기준으로 적용
States
state stores: 스트림 처리 어플리케이션에서 상태를 저장 또는 조회하는데 사용 할 수 있음
카프카 스트림은 모든 작업에서 하나이상의 상태 저장소를 만든다 (API 를 통해 저장 또는 조회 가능한)
지속적인 키/값 쌍, 인메모리 기반의 해쉬맵, 그외 편이한 데이터 구조 등으로 저장 가능
상태 저장소의 장애 내구성 보장 및 자동 복구를 제공
ARCHITECTURE
카프카 스트림은 카프카 프로듀서와 컨슈머 라이브러리 빌딩하고, 병렬화, 분산 조정, 장애내구성, 연산 단순화 등의 네이티브 기능 제공을 이용함으로써 어플리케이션 개발을 단순화 한다
Stream Partitions and Tasks
카프카 메시징 레이어는 데이터 저장 및 전송을 위한 파티셔닝을 수행
카프카 스트림은 데이터 처리를 위한 파티셔닝을 수행
위 파티셔닝은 데이터의 지역성, 유연성, 확장성, 고성능, 장애 내구성 등이 가능하게 해줌
카프카 스트림은 파티션과 타스크 개념을 카프카 토픽에 근거한 병렬 모델의 논리 단위로 사용함
병렬처리 상황에서 카프카 스트림과 카프카 사이에는 밀접한 관계가 있음
각 스트림 파티션은 완전하게 순서화 된 데이터 레코드의 순서이며, 카프카 파티션으로 매핑 된다
스트림 안에 하나의 데이터 레코드는 해당 토픽으로부터 하나의 카프카 메시지로 매핑 된다
데이터 레코드의 키는 카프카와 카프카 스트림 안에 데이터 파티셔닝을 결정한다
하나의 어플리케이션의 프로세서 토폴로지는 다수의 타스크들로 나누어진다
카프카 스트림은 어플리케이션에 대한 입력 스트림 파티션 기반으로 고정 타스크 수를 생성한다
각 타스크는 입력 스트림으로부터 파티션 리스트를 할당한다
각 타스크는 어플리케이션의 병렬 처리 단위로 사용하기 위해서 타스크들에 할당된 파티션들은 변경 되지 않는다
파티션을 할당받은 타스크들은 파티션을 기준으로 자기 소유의 프로세서 토폴로지를 인스턴스화 한다
타스크들은 할당받은 각 파티션에 대한 하나의 버퍼를 유지하고, 이 버퍼로부터 한번에 하나의 레코드를 처리 한다
이러한 결과로 스트림 타스크들은 수동 개입 없이, 독립적이고 병렬척으로 처리 된다
카프카 스트림은 리스소 메니저가 아니며, 스트림 어플리케이션이 동작하는 어떤곳에서는 동작하나는 하나의 라이브러리이다
어플리케이션의 다양한 인스턴스들은 동일한 머신이나 다양한 머신을 넘나들며 실행 되어지며, 타스크들은 동작하는 어플리케이션 인스턴스들에 라이브러리를 통해 자동적으로 분산화 되어진다
만약 하나의 어플리케이션 인스턴스가 실패하면, 할당 되어진 모든 타스크들은 자동적으로 다른 인스턴스 위에서 재실행 되어지며, 같은 스트림 파티션으로부터 소비를 지속한다
그림2 (stream partitions and tasks)
Threading Model
카프카 스트림은 사용자가 하나의 어플리케이션 인스턴스 안에서 라이브러리가 병렬 처리를 사용할 수 있도록 스레드 수를 설정하도록 허용
스트림 스레드나 어플리케이션 인스턴스를 늘리는 것은 토폴로지 복제를 늘리고, 카프카 파티션의 하나의 다른 서브셋을 처리하는 것
스레들 사이의 상태를 공유하지 않아, 스레드 사이에 조정이 필요 없다 (카프카의 coordination functionality 를 이용)
카프카 스트림 어플리케이션을 확장하는 것은 단순히 추가적인 어플리케이션의 인스턴스를 실행하면 된다 (어플리케이션 인스턴스 안에 실행되는 테스크들의 파티션 분산을 카프카 스트림이 알아서 수행)
Local State Stores
- 그림 3 (Local State Store)
Fault Tolerance
카프카 스트림은 카프카 통합 내재 된 장애 내구성을 갖는 기반으로 설계 되어 있음
카프카 파티션은 고가용성과 복제 되어져서 어플리케이션이 실패하여 재처리가 필요할 경우라도 지속적으로 이용 가능하다
로컬 상태 저장소 역시 장애 대응에 강건함을 보증한다
각 상태 저장소에 대해, 상태 업데이트를 추적하여, 복제 된 변화 로그 카프카 토픽을 유지 관리한다
이러한 변화로그 토픽들은 각 로컬 상태 저장소 인스턴스를 위해 파티션 되어져 있고, 저장소를 접근하는 작업은 전용의 변화로그 토픽 파티션을 갖는다
로그 컴팩션을 변화로그 토픽에 대해 활성화하여, 구데이터를 안전하게 소거할 수 있다 (토픽들을 무한정 커지는 것을 방지)
하나의 머신에서 실행 중인 작업이 실패하여, 다른 머신에서 재기동 할때에도, 카프카 스트림은 실패 이전에 상태 정보를 복원하는 것을 보장 한다 (새로운 작업을 실행하기 전에 일치하는 변화로그를 다시 재생하여)
이러한 결과로 장애처리는 최종 사용자에게 완전히 직관적이다
타스크 (재)초기화 비용은 일반적으로 변화로그 토픽들과 관련 되어진 상태 저장소를 다시 재생하여 상태를 복원하는 시간으로 결정 된다
이러한 시간을 최소화하기 위해, 사용자는 그들의 어플리케이션이 로컬 저장소의 대기 레플리카를 갖도록 설정할 수 있다
DEVELOPER GUIDE
Low-Level Processor API
- Processor
- Processor Topology
- State Stores
High-Level Streams DSL
Duality of Streams and Tables
Create Source Streams from Kafka
Windowing a stream
Join multiple streams
Transform a stream
Write streams back to Kafka
Application Configuration and Execution
UPGRADE GUIDE AND API CHANGES
Notable changes in 0.10.2.1
Streams API changes in 0.10.2.0
Streams API changes in 0.10.1.0