Event 의 특징
- 어떤 상태에 변화가 생겼을 때 이벤트를 발생시킨다.
- 이벤트는 immutable 하다. Producer 에 의해 변경되거나 삭제될 수 없다.
- 생성된 순서를 보장한다.
- 이벤트에 관심이 있는 개체는 이벤트가 발생했을 때 이를 감지하고 비즈니스 로직을 수행한다.
EDA(Event Driven Architecture) 의 구조
EDA (Event Driven Architecture) 는 아래와 같이 이벤트를 발생시키는 Producer, 이벤트를 감지하는 Consumer, Producer 와 Consumer 사이에 이벤트를 전달하는 message backbone 으로 구성되어 있다. 이런 구조는 하나의 시스템 안에서 각자의 마이크로서비스들이 loosely coupled 하게 서로의 정보를 교환하도록 해준다.
Message backbone 으로는 RabbitMQ 같은 publish/subscribe 형태의 message broker 또는 Apache Kafka 같은 event streamer 를 사용한다.
publish/subscribe 모델에서 message broker 는 여러 consumer 들이 동일한 토픽을 구독하는 게 가능하다. 모든 consumer 들이 메세지를 받고나면 메세지는 삭제된다.
반면에 event streamer 모델에서 consumer 들은 오프셋을 사용해서 event stream 내에 어느 이벤트까지 처리했는지 트래킹한다. event streamer 에서는 consumer 들이 같은 이벤트 메세지를 여러 번 가져갈 수 있고 이론 상으로 이벤트는 스트림에 영원히 머무를 수 있다. 이는 새로 추가된 consumer 가 스트림에서 가장 처음에 발생한 이벤트부터 읽을 수도 있다는 뜻이다. (더 자세한 내용은 여기 를 참고)
두 가지 모델 중 어느 것을 사용할 지는 이벤트의 발생 빈도, 데이터의 지속성 필요 여부, producer 의 특성에 따라 선택해야 한다.
Event-Driven Architecture 의 장점
producer & consumer decoupling 이 가능하다
event driven architecture 에서 producer 는 자신들이 내보낸 이벤트를 각 consumer 들이 어떻게 처리할 것인지 신경쓰지 않아도 된다. 따라서 producer 에 영향을 주지 않고 consumer 를 추가할 수 있다.
REST API 로 정보를 교환한다면 producer의 API를 호출하는 consumer 가 늘어날 때 producer 를 scale up 해야하는 비용이 발생할 수 있다.
EDA 에서 consumer 가 늘어나면 message backbone 사양을 늘릴 필요가 있겠지만 REST 모델에 비해 producer와 consumer 각자가 독립적으로 scale up/out 이 가능하다는 데 의미가 있다.
시스템 회복성이 좋다
어떤 consumer 마이크로서비스에 장애가 발생했다고 하자. EDA 에서는 consumer 가 다운되어도 producer 에서는 consumer의 상태와 상관없이 이벤트를 message backbone 에 쌓는다. 따라서 다운되었던 consumer 가 회복하고 나면 장애로 인해 처리를 실패했던 이벤트부터 replay 할 수 있다.
REST 모델에서는 네트워크가 유실되거나 producer 내부에 오류가 발생할 것을 대비하여 consumer 쪽에서 retry 로직을 넣어야 하는 데 비해 EDA 에서는 그럴 필요가 없다.
이런 특징은 오프라인 되기 쉬운 서비스에 적합하다. 예를 들어 producer가 이벤트를 발생시키면 사용자 앱에 푸시 알림을 하는 consumer 가 있다고 하자. 사용자가 기기를 꺼놓는 동안에 consumer는 아무 일을 하지 않고 producer 쪽에서는 이벤트를 message backbone 에 계속 쌓는다. 사용자가 기기를 켰을 때 그 동안 쌓였던 메세지를 consumer 가 replay 하여 차례대로 앱에 푸시를 하는 것.
polling 을 하지 않아도 된다
EDA 에서 클라이언트는 어떤 요청에 대한 결과를 곧바로 받고자 할 때 polling 을 할 필요 없이 producer의 push 를 받기만 하면 된다. 따라서 network IO 비용이 감소한다.
event sourcing 패턴이 single source of truth 를 가능하게 한다
single source of truth : 정보 모델과 연관 데이터를 오직 한 곳에서만 생성/수정하는 관례. 어떤 서비스가 데이터를 공급하면 제어도 해당 서비스에서 해야 한다. 이렇게 하면 어떤 데이터가 너무 오래되었거나 잘못되었을 때 이 데이터가 올바른 것인지 판단하기 위해 데이터를 공급/제어하는 서비스(source of truth)를 참조하기만 하면 된다.
Event의 특징에서 언급한 것처럼 비즈니스에서 어떤 개체의 상태에 변화가 생겼을 때 새로운 이벤트가 발생하고, 이 이벤트는 변경할 수 없다. 따라서 하나의 이벤트는 하나의 사실을 대표한다.
EDA 에서는 event sourcing 이라는 패턴을 사용하는데, 위 그림에서 보이는 것처럼 비즈니스 객체 (
Customer
, Item
, Address
에 해당)에 변화가 생겼을 때 consumer 는 객체의 최종 상태만을 스토리지에 저장하는 게 아니라 이벤트 자체를 스토리지에 쌓는 것을 말한다. 따라서 이벤트 소싱을 받는 consumer 의 스토리지에는 UPDATE, DELETE 가 발생하지 않고 CREATE 만 발생한다.Item
을 예로 들어서, Customer
들이 Item
을 구매할 때마다 Item
의 재고가 줄어드는데 consumer 입장에서는 item_count -= 1
와 같은 로직으로 최종 재고 수량만을 스토리지에 UPDATE 하는 게 아니라customer 1, item 1
customer 3, item 2
customer 1, item 3
과 같이 상태 자체를 스토리지에 저장하고,
Item
의 재고를 조회할 때 이 row들을 집계해서 재고 수량을 제공하는 것이다.이러한 패턴에서는 이벤트가 아주 빈번하게 발생하는만큼 스토리지에 쌓이는 row 가 많아지기 때문에 최종 상태를 가져오는 데 시간이 오래 걸릴 수 있다. 따라서 스냅샷(snapshot) 개념을 도입한다. 특정 수 만큼 이벤트가 발생했을 때 그 때의 최종 상태를 스냅샷으로 저장해놓고, 그 이후에 조회할 때는 스냅샷 이후에 발생한 이벤트들만 집계하는 것.
이애플리케이션을 재시작해서 비즈니스 객체를 가장 최근의 상태로 생성해야 할 때 event sourcing 을 받은 스토리지가 source of truth 가 된다.
event sourcing 은 race codition 이 발생할 가능성을 없애며, race condition 을 방지하기 위해 RDBMS 를 사용하는 경우 isolation level 을 올려야 하는 비용을 들이지 않아도 된다.
Event-Driven Architecture 의 단점
비즈니스 흐름을 파악하기 어렵다
producer 와 consumer 가 decoupling 되는만큼 비즈니스 흐름을 이해하기 어렵게 한다. (시스템의 규모가 커지면 여러 event notification 들이 하나의 비즈니스 로직을 구성하게 되는데 코드 상으로는 이를 알아보기가 어렵다.) 이는 문제가 발생했을 때 문제의 근간을 파악하는 데 장애가 될 수 있다. 따라서 각 마이크로서비스를 모니터링하는 게 중요하고 시스템 흐름을 이해할 수 있도록 문서화하는 데 신경 써야 한다.
consumer 의 데이터 일관성을 보장하지 못할 수 있다
어떤 마이크로서비스
A
에서 Item
등록과 삭제 이벤트를 produce 하고 B
는 이를 consume 한다고 하자. B
는 항상 아이템 등록 이벤트 이후에 삭제 이벤트가 발생하는 것을 예상하고 있으나 네트워크 상의 이유로 삭제 이벤트가 등록 이벤트보다 먼저 consume 될 수 있다. 일관성을 유지해야 하는 B
는 이러한 예외를 감안해야 하며 이는 B의 코드 복잡도를 높일 수 있다. 출처