이벤트 스트림을 설계하는 방법, 3부
이전 1부 및 2부를 참조하세요.
당신 사이의 관계 이벤트 정의 그리고 이벤트 스트림 그 자체가 주요 디자인입니다. 제가 받는 가장 일반적인 질문 중 하나는 “여러 이벤트 유형을 하나의 스트림에 넣어도 괜찮나요?”입니다. 아니면 각 이벤트 유형을 자체 스트림에 게시해야 합니까?”
이 문서에서는 이러한 질문에 답하는 데 도움이 되는 요소를 살펴보고 자신의 사용 사례에 가장 적합한 답변을 찾는 데 도움이 되는 일련의 권장 사항을 제공합니다.
소비자 사용 사례의 예
- 상태 변경에 대한 일반 경고(델타)
- 이벤트 처리 순서(델타)
- 상태(사실) 전달
- 사실과 델타의 혼합
이벤트 스트림을 구성하는 방법을 결정할 때 소비자의 사용 사례를 가장 먼저 고려해야 합니다. 이벤트 스트림은 한 번만 기록되지만 다양한 소비자가 여러 번 읽을 수 있는 재생 가능한 데이터 소스입니다.
우리는 그들이 자신의 필요에 따라 데이터를 최대한 쉽게 사용할 수 있도록 하고 싶습니다.
사용 사례: 변경 사항에 대한 일반 경고
델타는 일반적인 변경 알림에 매우 적합합니다. 애플리케이션은 애플리케이션 내부에서 노출된 델타 이벤트에 응답할 수 있습니다. 스트림당 하나의 유형만 있도록 이벤트를 분할하면 높은 세분성을 제공하고 소비자 애플리케이션이 관심 있는 델타만 구독할 수 있습니다.
사용 사례: 델타 이벤트 처리 시퀀스
그러나 단일 애플리케이션이 여러 델타를 읽어야 하고 이벤트 간의 순서가 매우 중요하다면 어떻게 될까요? 스트림당 하나의 이벤트 전략을 따르면 이벤트를 순서대로 읽고 처리하여 일관되지 않은 순서 결과를 제공할 수 있는 위험이 있습니다.
Kafka Streams 및 Flink와 같은 스트림 프로세서에는 일반적으로 오름차순 타임스탬프와 오프셋 순서로 이벤트를 처리하는 논리가 포함되어 있습니다. 이 프로세스를 “이벤트 예약”이라고 부릅니다. 예를 들어 Kafka Streams는 최선의 알고리즘을 사용하여 처리할 다음 레코드를 선택하는 반면 Flink는 워터마킹 전략을 사용하여 타임스탬프를 기반으로 레코드를 처리합니다. 모든 스트림 처리 프레임워크가 이벤트 예약을 지원하는 것은 아니므로 경쟁 조건 결과에 따라 처리 순서가 크게 달라질 수 있다는 점에 유의하세요.
결국 이벤트 일정을 잡는 것조차 최선의 노력입니다. 애플리케이션이나 하드웨어의 간헐적인 오류뿐 아니라 프레임워크 자체의 해결되지 않은 코너 케이스 및 경합 조건으로 인해 잘못된 처리가 여전히 발생할 수 있습니다.
이것은 보이는 것만큼 심각하지 않다는 점에 유의하십시오. 스트리밍 사용 사례의 대부분은 주제 간 처리 순서에 그다지 민감하지 않습니다. 민감한 사람들을 위해 워터마킹과 이벤트 예약은 대부분의 경우에 꽤 잘 작동하는 경향이 있습니다. 그리고 완벽하게 엄격한 순서가 필요한 이벤트 시퀀스의 경우? 자, 계속 읽어보세요.
엄격한 주문은 어떻습니까?
하지만 더 강력한 보장이 필요한 경우에는 어떻게 합니까? 정확하고 엄격한 이벤트 순서는 비즈니스 사용 사례에 중요한 요소가 될 수 있습니다.
이 경우 소비자가 작성된 순서와 동일한 순서로 이벤트를 수신할 수 있도록 모든 이벤트를 단일 이벤트 스트림에 넣는 것이 더 나을 수 있습니다. 또한 Kafka는 파티션별로 순서만 보장하므로 동일한 키의 모든 이벤트가 동일한 파티션으로 이동하도록 일관된 파티셔닝 전략이 필요합니다.
이 기술은 사용 중인 토픽 수를 줄이는 것이 아닙니다. 토픽은 상대적으로 저렴하며, 전달하는 데이터와 제공할 목적을 기반으로 토픽을 구축하도록 선택해야 합니다. 단순히 주제 수를 줄이세요. Apache Kafka는 수천 개의 주제를 문제 없이 완벽하게 처리할 수 있습니다.
단일 스트림, 다중 델타 유형
관련 이벤트 유형을 단일 토픽 파티션에 배치하면 소비자 처리에 대한 엄격한 증분 순서가 제공되지만 이벤트 순서에 대한 엄격한 제어가 필요하므로 단일 생산자가 모든 이벤트를 작성해야 합니다.
이 예에서는 장바구니에 대한 모든 추가, 제거 및 할인 코드를 단일 이벤트 스트림의 단일 파티션으로 병합했습니다.
사용 사례: 델타 이벤트 처리 시퀀스
다시 축소하면 이 이벤트 스트림과 결합된 단일 소비자를 볼 수 있습니다. 스트림의 각 유형을 이해하고 해석할 수 있어야 합니다. 귀하의 주제를 다양한 이벤트 유형의 쓰레기장으로 전환하지 않고 소비자가 간단히 알아낼 것이라고 기대하지 않는 것이 중요합니다. 오히려 소비자는 각 델타 유형을 처리하는 방법을 알아야 하며, 새로운 유형이나 기존 유형에 대한 변경 사항은 애플리케이션 소유자 간에 협상되어야 합니다.
Flink SQL을 사용하여 스트림 분할
또한 Flink와 같은 스트림 프로세서를 사용하여 단일 카트 이벤트 스트림을 델타당 이벤트 스트림으로 분할하여 각 이벤트를 새 주제에 쓸 수도 있습니다. 소비자는 이러한 특수 목적의 델타 스트림을 구독하도록 선택하거나 원본 스트림을 구독하고 관심 없는 이벤트를 간단히 필터링할 수 있습니다.
주의의 말씀
그러나 주의할 점이 있습니다. 이 패턴은 생산자와 소비자 서비스 간에 매우 강력한 결합을 가져올 수 있습니다. 일반적으로 이는 이벤트 소싱을 사용하는 한 쌍의 시스템과 같이 강력하게 결합되도록 의도된 애플리케이션에만 적합하며 범용 용도에는 적합하지 않습니다. 또한 이 두 애플리케이션을 분리할 가치가 있는지, 아니면 단일 애플리케이션으로 재설계해야 하는지 스스로에게 물어봐야 합니다.
사용 사례: 사실을 사용하여 상태 전송
사실은 Event-Carried State Transfer라는 기능을 제공합니다. 각 이벤트는 특정 시점의 특정 엔터티 상태에 대한 전체 보기를 제공합니다.
사실 이벤트는 상태 전송을 위한 훨씬 더 나은 옵션을 제공하고 소비자가 이벤트 시퀀스를 해석할 것을 요구하지 않으며 훨씬 더 느슨한 결합 옵션을 제공합니다. 이 경우 이벤트 스트림당 단일 이벤트 유형만 사용됩니다. 즉, 다양한 스트림의 사실이 혼합되지 않습니다.
스트림당 하나의 팩트 유형만 유지하면 액세스가 필요한 모든 애플리케이션에 읽기 전용 상태를 훨씬 쉽게 전송할 수 있습니다. Streams of Facts는 비즈니스 문제 해결을 위해 특별히 제작된 애플리케이션과 서비스를 구성하기 위한 데이터 빌딩 블록 역할을 효과적으로 수행합니다.
스트림당 단일 팩트 유형
Kafka Streams 또는 Flink와 같이 애플리케이션을 구축할 수 있는 도구를 살펴보면 스트림당 하나의 사실 유형이라는 관례가 다시 나타납니다.
이 예에서 Flink SQL 애플리케이션은 항목 팩트를 테이블로 구체화합니다. 쿼리는 테이블 스키마, Kafka 주제 소스, 키 열, 키 및 값 스키마 형식을 지정합니다.
Flink SQL은 엄격한 스키마 정의를 적용하고 이를 준수하지 않는 수신 이벤트를 삭제합니다. 이는 테이블 스키마 요구 사항을 충족하지 않는 데이터를 삽입하려고 하면 관계형 데이터베이스에서 예외가 발생하는 방식과 동일합니다.
서로 다른 사실 스트림에 합류
다양한 스트림에서 여러 유형의 팩트를 사용할 때 Flink SQL의 조인 기능을 활용하여 자체 비즈니스 로직에 필요한 필드만 선택하고 나머지는 삭제할 수 있습니다. 이 예에서 Flink SQL 애플리케이션은 재고와 품목 팩트를 모두 사용하고 ID, 가격, 이름 및 재고만 선택하지만 재고가 있는 품목이 하나 이상 있는 레코드만 유지합니다. 데이터는 필터링되고 함께 결합된 다음 재고 항목 팩트 스트림으로 내보내지며, 필요한 모든 애플리케이션에서 사용할 수 있습니다.
모범 사례: 전체 상태를 하나의 사실로 기록
이벤트를 녹화할 때 발생한 모든 일을 하나의 세부 이벤트로 유지하는 것이 중요합니다.
고려 주문하다 (위) 이는 두 가지로 구성됩니다. 카트 엔터티와 사용자 실재. 주문 이벤트 생성 시 해당 시점의 사용자에 대한 모든 장바구니 정보는 물론, 모든 사용자 정보를 삽입합니다. 우리는 발생한 일을 정확하게 표현하기 위해 이벤트를 단일 원자 메시지로 기록합니다. 우리는 ~ 아니다 여러 다른 주제의 여러 이벤트로 나누어 보세요!
소비자는 이벤트에서 실제로 원하는 데이터만 자유롭게 선택할 수 있으며, 복합 이벤트는 언제든지 분할할 수 있습니다. 그러나 처음부터 분할하면 원래 이벤트를 재구성하는 것이 훨씬 더 어렵습니다.
가장 좋은 방법은 초기 이벤트에 고유 ID를 부여한 다음 이를 모든 파생 이벤트에 전파하는 것입니다. 이는 이벤트 추적을 제공합니다. 이벤트 ID에 대해서는 향후 게시물에서 더 자세히 다루겠습니다.
사용 사례: 사실과 델타 혼합
소비자는 필요한 팩트 스트림을 선택하고 선택한 델타와 결합하여 애플리케이션을 구성할 수도 있습니다.
이 접근 방식은 각 소비자의 요구 사항에 따라 데이터를 쉽게 혼합할 수 있으므로 이벤트 스트림당 단일 유형에 가장 적합합니다.
요약
단일 델타 유형의 단일 스트림을 사용하면 애플리케이션이 특정 에지 조건에 쉽게 응답할 수 있지만 자체 상태를 구축하고 자체 비즈니스 논리를 적용하는 책임은 그대로 유지됩니다.
스트림당 단일 델타는 상태의 완벽한 집계를 생성하려고 할 때 어려움을 초래할 수 있습니다. 집계를 구축하기 위해 고려해야 할 이벤트를 파악하려고 할 때도 어려울 수 있습니다.
일련의 델타에서 집계를 구축하는 등 엄격한 순서가 우려되는 경우 동일한 스트림에 여러 이벤트 유형을 배치합니다. 최종 상태를 구성하는 데 필요한 모든 데이터가 제작자가 게시한 순서와 정확히 동일한 순서로 갖게 됩니다.
단점은 별로 신경 쓰지 않는 이벤트라도 각 이벤트를 소비하고 처리해야 한다는 것입니다.
마지막으로 팩트 스트림에 단일 이벤트 유형을 사용합니다. 이는 테이블당 하나의 잘 정의된 스키마를 사용하여 이 정보를 관계형 데이터베이스에 저장하는 방법과 동일합니다. 스트림 소비자는 Kafka Streams 또는 Flink와 같은 도구를 사용하여 자신의 사용 사례에 필요한 팩트 이벤트를 혼합, 일치 및 혼합할 수 있습니다.
이 이벤트 디자인 시리즈에는 한 부분이 더 있습니다. 워크플로, 다양한 모범 사례, 스키마 발전의 기본 사항 등의 요소를 다루면서 마무리할 다음 내용을 계속 지켜봐 주시기 바랍니다.
Post Comment