스트림 프로세싱 관련 글

1.스트림 프로세싱 패턴들
2.스트림 프로세싱의 확장성을 확보하고 성능을 최적화하기 위한 패턴들
3.스트림 프로세싱 서버가 터져도 살아남는 법 (aka 신뢰성 확보)

확장성 및 성능 최적화 패턴

스트림을 처리하는 클라우드 네이티브 애플리케이션은 독특한 확장성과 성능에 대한 요구사항을 가지고 있습니다. 예를 들면 애플리케이션이 이벤트를 처리하는 동안 이벤트의 순서를 지켜야한다는 점이 있죠. 이런 애플리케이션은 주로 메모리에 상태를 저장하기 때문에 정확도를 해치지 않으면서 이벤트를 처리할 수 있는 확장전략을 마련해야합니다.

1) 순차 수송 패턴

이벤트를 다양한 분류로 나누고 병렬로 처리하는 방식으로 클라우드 네이티브 애플리케이션을 확장합니다. 또한 이벤트 순서를 영구적으로 저장해서 나중에 이벤트들을 다시 합칠 수 있기 때문에 이벤트의 원래 순서를 보장합니다.

어떻게 동작할까요

이벤트를 각각의 특성이나 값에 따라 그룹으로 나누어서 이들을 병렬로 처리합니다.
각 이벤트는 분리하기 전 순번을 매깁니다. 병렬로 처리할 때에도 이벤트를 순서대로 진행할 수 있습니다.(윈도우드 애그리게이션이나 임시 이벤트 순차 정렬 패턴이 이벤트의 순서를 보장해주어야하는 대표적인 패턴입니다.) 또한 이 순번을 가지고 나중에 병렬 처리한 이벤트 스트림을 합칠 때 다시 순서대로 이벤트를 합칠 수 있습니다.

병렬 처리 완료 되었으나 자신의 순서가 아닌 이벤트들은 병렬 스트림에 머무르다가 순서가 되면 출력됩니다. 이 때 메시지 브로커나 이벤트 큐는 이벤트들을 나누고 하위 스트림으로 나누며 이벤트들을 저장해서 단일 스트림으로 다시 합치는 역할을 담당합니다.

어떻게 사용할 수 있나요

스트림 처리 애플리케이션의 확장

  • 순차 수송 패턴을 사용하면 클라우드 네이티브 애플리케이션의 CPU, 메모리, 네트워크 대역폭과 같은 제약을 극복할 수 있음
  • 각 이벤트 처리 마이크로서비스가 이벤트의 고객 ID를 기반으로 데이터 스토어 검색을 통해 데이터에 추가 내용을 덧붙인다고 가정. 데이터 스토어에서 데이터를 조회하고 가져오는 데 시간이 소요되며 잠재적으로 전체 처리 시간에 지연을 초래하는 경우, 성능 향상을 위해 마이크로서비스에 데이터를 캐싱. 하지만 모든 데이터를 캐싱할 수 없기 때문에 결국 캐시미스로 인한 지연 시간이 발생.
    -> 이 경우 순차 수송 패턴을 사용해서 각 이벤트를 고객 ID의 해시 값에 기반하여 여러 개의 하위 스트림으로 나눔. 같은 고객 ID를 가지는 이벤트는 계속 같은 마이크로서비스에서 처리하게 될 것이며, 따라서 캐시 적중률이 증가하게 됨.

스트림 처리 분할

  • 고객의 등급과 주문 금액 크기에 따라 물건의 배송 시간을 다르게 보장해주는 애플리케이션
  • 유형별로 당일 배송, 익일 배송, 주중 배송 과 같은 배송 형태를 보장

고려해야 할 사항들

의미 있는 스트림 처리를 위해서는 이벤트 속성 값에 따라 이벤트를 분류할 수 있어야합니다. 대개는 여러 속성 값을 조합하여 분류하고 병렬처리 합니다.

이벤트를 여러 하위 스트림으로 나누면 각각의 하위 스트림은 서로 다른 클라우드 네이티브 애플리케이션을 통해 처리할 수 있습니다. 그 과정에서 이벤트가 걸러지고 삭제될 수 있고, 이벤트 순번이 연속적이지 않게 됩니다. 이 경우 이벤트들을 다시 합칠 때 모든 순번이 다 있지 않다는 사실에 유념해야합니다. 어떤 이벤트가 제거되었는지 확실하게 알아낼 방법이 없기 때문에, 타임아웃을 적용해서 이벤트가 제거되었는지 여부를 판단하는 것이 좋습니다.

이벤트를 다시 합치는 더 좋은 방법은
1) 순차 이벤트가 끝났다는 메시지를 보내는 것입니다. 이벤트를 처리하는 각 애플리케이션이 주기적으로 가장 마지막으로 처리한 이벤트의 메시지 ID를 보내는 것입니다. 이벤트를 다시 합치는 애플리케이션은 이 ID를 토대로 하위 스트림을 처리하는 애플리케이션이 이전 순번의 이벤트를 모두 처리하였으며 따라서 마지막 메시지 ID보다 순번이 낮은 이벤트가 도착하지 않았다면 제거되었다고 볼 수 있습니다. 그리고 제거된 순번 이후 ID를 가지는 이벤트들이 처리되지 않고 대기중인 상태를 해소할 수 있습니다.
2) 순번을 토대로 이벤트를 다시 합칠 수 없는 경우는 이벤트들을 합쳐서 단일 토픽으로 다시 발행할 수 있습니다. 그리고 버퍼 이벤트 순차 정렬 패턴을 사용해서 이벤트를 저장하고 순번 또는 이벤트 발행 시각을 토대로 다시 정렬할 수 있습니다.

스트림 처리 마이크로서비스들이 병목 지점이 된다면 스트림 수를 확장할 수 있는 설계를 하는 것이 좋습니다. 이벤트 분류 방법을 바꿔서 부하가 신한 하위 스트림을 더 많은 하위 스트림으로 나누고 병렬 처리하는 방법이 있습니다.

관련 패턴들

  • 생산자-소비자 및 발행자-구독자 패턴: 순차 수송 패턴을 구현할 때 사용
  • 버퍼 이벤트 순차 정렬 패턴: 여러 이벤트 스트림을 합쳐 순서대로 정렬
  • 주기적인 상태 스냅숏 저장 패턴: 하위 스트림을 처리하는 애플리케이션의 상태를 저장해서 애플리케이션 확장을 가능하도록 만듦

2) 버퍼 이벤트 순차 정렬 패턴

이벤트는 네트워크 지연이나 연결 재시도 등의 문제로 순서에 맞지 않게 도착할 수 있습니다. 버퍼 이벤트 순차 정렬 패턴을 사용해서 이벤트를 사용하기 전에 순서대로 정렬합니다.

어떻게 동작할까요

이벤트를 순서대로 정렬하려면, 이벤트에 순차로 증가하는 값을 기록해야만 합니다. 이런 숫자값은 순번이 될 수도 있고 시각이 될 수도 있습니다. 순번은 계속 증가하는 값이며 스트림의 각 이벤트가 고유한 순번을 가지도록 보장해야합니다. 하지만 시각을 사용하는 경우 여러 개의 이벤트가 같은 시각에 생성될 수 있기 때문에 모든 이벤트가 고유한 시각 값을 가진다고 보장할 수 없습니다.

순번을 사용한 순차 정렬 예를 봅시다.
이벤트가 seq.7 -> seq.8 -> seq.10 -> seq.9 순서대로 들어온다고 합시다.


입력스트림 |||| 이벤트 순차 정렬 버퍼 |||| 정렬한 스트림
[ seq.9 ] –> [ seq.10] –> [ seq.8 seq.7 ]


1) 8 뒤에 10은 순서가 아니므로 버퍼에서 타임 아웃 시간동안 기다립니다. 9번이 시간 내에 도착하면 9, 10을 함께 전달합니다. 타임 아웃 시간까지 9번이 도착하지 않는다면 10번을 먼저 보냅니다.
2) 9번은 여러 이유로 스트림에서 빠질 수 있습니다. 이런 경우 이전 이벤트를 처리하는 시스템이 9번을 빈 이벤트로 보낼 수 있습니다. 그렇다면 타임 아웃 시간까지 기다리지 않아도 됩니다.
3) 또한 이전 이벤트를 처리하는 시스템이 10번 이전의 모든 이벤트를 다 처리했다는 내용을 추가해서 이벤트 순차 정렬 버퍼로 보낼 수 있습니다. 그러면 순차 정렬 애플리케이션은 별도의 지연 없이 빠진 이벤트를 파악할 수 있습니다.

타임 아웃 시간은 기존 데이터를 통해 적당한 값으로 정해야합니다 K-slack, AQ-Kslack 알고리즘을 참고할 수 있습니다.

어떻게 사용할 수 있나요

이 패턴은 이벤트를 정렬해야 하는 애플리케이션과 함께 배포해서 사용할 수 있습니다.

분산 생성된 이벤트들 순차 정렬

  • 여러 곳에서 사람의 움직임을 감지하는 센서가 생성한 이벤트들은 전송 지연 때문에 동시에 탐지한 내용도 서로 다른 시각에 도착할 수 있음.
  • 이벤트를 생성한 시각을 활용. 손서에서 벗어난 이벤트들은 메시지 브로커의 단일 토픽으로 전달
  • 마이크로서비스를 구현해서 이 이벤트들을 가져옴
  • 버퍼 이벤트 순차 정렬 패턴을 사용하여 이벤트 재정렬 후 후속 시스템에 전달

같은 원천에서 생성한 이벤트들을 재정렬

  • 성능 목표 달성을 위해 이벤트 병령로 분산 처리.
  • 브라우저에서의 사용자 상호작용을 병렬로 처리한 후 이벤트를 합쳐 후속 시스템에 전달.
  • 사용자의 모든 행동은 같은 브라우저에서 기록하고 이벤트를 생성하기 때문에 이벤트 생성 시각을 사용하는 것보다 고유한 순번을 매기는 것이 좋음 ??

고려해야 할 사항들

이 패턴은 시간에 따른 이벤트 애그리게이션이나 이벤트 순번을 탐지해야할 필요가 있을 때 사용하면 좋습니다. 이외에는 지연시간이 추가되어 병목 지점이 될 수 있습니다.

이벤트가 단일 원천에서 생성된 경우 이벤트를 높은 정확도로 재정렬할 수 있습니다. 이벤트가 분산되어 생성되는 경우 각 원천들의 시각이 동기화되었다고 보기 어렵기 때문에 이벤트 생성 시각을 기준으로 이벤트를 정렬하면 이벤트가 원래 순서대로 재정렬 될 것이라고 보장할 수 없습니다. 생성 시각 대신 고유한 이벤트 순번을 사용하도록 합니다. (시각을 기준으로 하는 것보다 순번을 기준으로 하는 것이 정렬 지연시간이 더 짧기도 합니다.)

먼저 생성된 이벤트가 더 늦게 도착하는 경우, 이 이벤트를 순번에 어긋난 형태로 전달할지 또는 제거할지 결정해야합니다. 생산 공정에서 현재 라인의 온도를 측정하는 이벤트 같은 경우 오래된 이벤트를 버린다고 문제될 것은 없습니다. 하지만 부정 사용 탐지를 위해 신용카드 트랜잭션 이벤트를 사용하는 경우 트랜잭션을 버려서는 안됩니다.

관련 패턴들

  • 임시 이벤트 순차 정렬 및 윈도우드 애그리게이션 패턴: 이 패턴들에 버퍼 이벤트 순차 패턴 사용하면 더 정확한 결과 만들어낼 수 있음
  • 신뢰성 패턴: 버퍼 복구

3) 오류 수정 패턴

이벤트 분석 결과를 최대한 빠르게 처리하고 빠진 메시지나 이벤트가 뒤늦게 도착하면 이를 토대로 분석 내용을 수정하고 다시 알려줍니다. 정확도가 좀 떨어지지만 낮은 지연 속도로 빠르게 분석 결과를 알려주는 패턴입니다.

어떻게 동작할까요

이 패턴은 윈도우드 애그리게이션 패턴이나 임시 이벤트 순차 정렬 패턴과 함께 사용합니다. 모든 이벤트가 순서대로 도착할 때까지 기다리지 않고 도착하는 대로 처리해서 결과를 만듭니다. 그리고 나중에 도착한 이벤트를 발견하면 이 결과를 반영해서 그 결과를 변경하고 다시 알려줍니다. 이 패턴을 사용하려면 다운스트림 애플리케이션이 전달 받은 이벤트가 나중에 더 정확한 내용으로 부분적 업데이트 될 수 있다는 점을 알아야합니다.

어떻게 사용할 수 있나요

이 패턴은 이벤트를 순서대로 처리함과 동시에 낮은 지연 시간을 가져야 하고 초기에 낮은 정확도의 데이터를 빠르게 받아보고 나중에 변경해도 문제가 없는 애플리케이션에서만 사용할 수 있습니다.

결과를 최신 정보로 업데이트 하기

  • 실시간으로 결과를 화면에 표시해줄 때 많이 사용
  • 이벤트들을 좀 더 오랜 시간 저장해 두고 나중에 늦게 도착한 이벤트가 발견될 경우 결과 데이터를 변경해서 다시 보냄
  • 최근 1분동안 도착한 주문 개수를 알려주는 애플리케이션
    • 1분 단위로 버킷을 만들고 해당 기간동안 도착하는 주문의 수를 계산하기 위해 카운터를 사용
    • 1분이 지나면 해당 버킷에서 계산한 결과를 내보냄
    • 다음 1분 동안 늦게 들어오는 이벤트를 반영하여 변경 내용을 함께 내보냄
  • 여러개의 버킷을 사용하면 대량의 메모리 공간을 차지하므로 버킷의 삭제 시점에 주의

이전에 만든 의사 결정 데이터 수정

  • 사용자가 요청해서 택시를 최대한 빠르게 보내야하는 상황
  • 사용자 요청이 들어오면 해당 지역 모든 택시에게 내용을 전달하고, 어느 택시 하나가 요청을 수락하면 나머지 모든 택시에 사용자 요청이 처리되었다는 사실을 알려줌. 이 과정에서 여러 택시가 요청을 수용했지만 네트워크 지연으로 인하여 이 사실을 나중에 알게 될 수도 있음. 이때, 이를 수정할 이벤트를 다시 보내서 다른 택시에 할당된 요청 사항을 취소하게 함.

고려해야 할 사항들

초기에 빠르게 만든 결과가 유용해야 하며 나중에 도착한 이벤트를 토대로 변경한 내용을 바탕으로 결과 데이터를 수정할 수 있는 경우 이 패턴을 사용할 수 있습니다. 오류 수정을 지원하지 않는 경우 버퍼 이벤트 순차 정렬 패턴과 같이 의사 결정을 이벤트가 도착할 때까지 지연시키는 패턴을 사용해야합니다.

대부분의 경우 이벤트들은 나중에 이벤트들이 늦게 도착하는 경우를 대비해서 메모리에 계속 저장되기 때문에 메모리를 많이 차지합니다. 메모리 자원이 고갈되지 않으면서도 시스템이 오래 이벤트를 기다릴 수 있는 좋은 균형점을 찾아야합니다.

관련 패턴들

  • 신뢰성 패턴: 애플리케이션이 이전에 만든 결과 데이터를 복구하여 사용
  • 버퍼 이벤트 순차 정렬 패턴: 오류 수정을 지원하지 않는 경우 이 패턴을 대신 사용
  • 임시 이벤트 순차 정렬 패턴 및 윈도우드 애그리게이션 패턴: 오류 수정 패턴과 함께 사용 가능

4) 워터마크 패턴

클라우드 네이티브 애플리케이션 안에서 이벤트 스트림을 통해 매쉬 구조 형태로 서로 연결된 여러 마이크로서비스 간에 스트림 처리를 주기적으로 정렬(=주기적으로 이벤트 처리가 동기화 됨) 해야 하는 경우 워터마크 패턴을 사용할 수 있습니다. 작업을 정렬함으로써 모든 마이크로 서비스가 특정 이벤트 보다 먼저 도착한 이벤트들을 전부 다 처리했는지 확인할 수 있습니다. 이때 참조하는 특정 이벤트를 일반적으로 워터마크 이벤트라고 부릅니다.

어떻게 동작할까요

워터마크 생성기가 주기적(입력 시스템들이 시간 기반으로 동기화 되어있어야 함)으로 워터마크 이벤트를 생성해서 클라우드 네이티브 애플리케이션 외부 입력지점으로 보내야합니다. 또한 워터마크 이벤트를 소비하는 중간 마이크로서비스들이 여러 이벤트 사이에서 같은 위치에 이 워터마크 이벤트를 다시 전달할 수 있도록 해야합니다. 모든 스트림으로부터 워터마크 이벤트를 받으면 해당 워터마크 이벤트를 종속 시스템으로 전달한 다음 다음 워터마크 이벤트를 받을 때까지 입력 스트림들로 부터 이벤트를 받아서 계속 처리합니다. 이 패턴은 각 워터마크 이벤트마다 이벤트 처리가 동기화 된다는 것을 보장합니다.

입력 시스템 간 시간 기반 동기화가 불가능하다면 전역 카운터 등을 도입해서 각 입력 소스가 해당 카운터에서 이벤트 순번을 가져오도록 만들 수도 있습니다.

어떻게 사용할 수 있나요

여러 입력 소스시스템으로부터 이벤트들이 제때 도착하지 않거나 네트워크 지연 혹은 처리 시간 등으로 이벤트 도착 시간에 영향을 주는 경우 이 패턴을 사용할 수 있습니다. 특히 이벤트 처리를 주기적으로 동기화 해서 오류를 줄이고자 할 때 이 패턴을 사용하면 좋습니다.

시간 기반 동기화된 여러 이벤트 소스로부터 생성된 이벤트들 동기화

  • 서버 팜의 여러 서버로 부터 값을 읽어서 처리하고 결과를 만드는 작업
    • 워터마크 사이의 이벤트들을 모아 애그리게이션 작업을 수행하면 작업 결과가 더 정확해짐

동기화 되지 않은 입력 소스로부터 생성된 이벤트들 동기화하기

  • 근처에 배포한 센서로부터 수직한 정보를 임시 이벤트 순차 정렬 패턴을 통해 정렬하고 특정 상황을 탐지하려함
    • 이벤트들이 분산된 센서로부터 발생하여, 센서 지연 때문에 이벤트를 늦게 보내는 경우, 네트워크 지연으로 늦게 도착하는 경우 등이 발생
    • 중앙 서버에 전역 카운터를 도입하여 센서가 읽어 들인 값을 이벤트로 만들기 전 전여가운터에서 이벤트 순번을 주기적으로 가져가도록 만듦

고려해야 할 사항들

이 패턴은 네트워크 지연 등으로 이벤트 도착 시각이 생성 시각과 큰 차이가 발생할 것이라는 것을 알 때나, 혹은 입력 시스템들이 시간 동기화가 맞지 않는 상황에서만 사용하는 것이 좋습니다. 그 외에는 적용해도 큰 이점이 없습니다.

이패턴은 주기적으로 시간을 동기화 해주지만, 여전히 한 스트림 내에서 발생한 이벤트가 다른 이벤트보다 늦게 도착할 수 있다는 문제가 남아있습니다.

관련 패턴들

  • 버퍼 이벤트 순차 정렬 패턴 및 오류 수정 패턴: 이벤트 도착 시각이 네트워크 지연 또는 다른 처리 시스템으로 인해 영향을 받지 않는 경우 이 패턴을 대신 사용
  • 임시 이벤트 순차 정렬 패턴 및 윈도우드 애그리게이션 패턴: 이 패턴들은 워터마크 패턴과 함께 사용해서 이벤트 순서대로 정렬하고 그 결과를 더 정확하게 만들 수 있음.
  • 주기적 상태 스냅숏 저장 패턴: 워터마크 패턴은 주기적 상태 스냅숏 패턴에서 필수 패턴으로 여러 스트림을 동기화하고 주기적으로 상태 스냅샷을 만들어 저장함.

정리

다음 포스팅에서는 스트림 프로세싱 패턴의 신뢰성을 확보하기 위한 신뢰성 패턴들을 알아보겠습니다.

Reference

클라우드 네이티브 애플리케이션 디자인 패턴 Chapter6. 스트림 프로세싱 패턴을 읽고 정리한 내용입니다.
자세한 내용은 책을 참고해주세요.