전 시간에는 마이크로서비스 간 통신 시 주의해야할 점과 동기통신, 비동기 통신에 대해 알아보았다. 오늘은 이벤트 주도 아키텍처에 대해 알아보려고 한다.
이벤트 주도 아키텍처
란 확장성이 뛰어난 애플리케이션을 생성하는데 널리 사용되는 분산 비동기 아키텍처이다.
이벤트 발생시 해당 이벤트 로그를 보관하고 이를 기반으로 동작하며 비동기 통신을 통해 시스템 내 통합(integration)을 수행하여 MSA의 단점을 보완하고자 등장하였다.
이벤트 주도 아키텍처
는 마이크로서비스 간 관계를 느슨하게 만들 수 있는 중요한 기법이다.
분산 아키텍처 환경에서 상호 간 결합도를 낮추기 위해 비동기 방식으로 메시지를 전달하며 주로 Message Broker(Kafka, RabbitMQ)와 결합하여 구성된다.
이벤트 주도 아키텍처
는 실생활에서도 많이 쓰이는 방식이다.
- 손님이 주문을 하게 되면 주문접수자는 주문을 받는다.
- 바리스타에게 커피제작을 의뢰한다.
- 그럼 바리스타는 커피주문 이벤트를 순차적으로 제작목록에 기입하고 주문한 종류에 해당하는 커피를 만든다.
- 그리고 커피가 다 만들어지면 커피제작완료 이벤트를 보내고 진동벨을 통해 손님에게 통보한다.
- 이벤트 기반의 방식은 여러 개의 주문을 받아 여러 개의 커피를 제작할 수 있는 효율성을 만들어 준다.
이벤트 기반 아키텍처는 이벤트를 생산하는 모듈과 이벤트에 대응하는 모듈을 분리하고 상호 독립적으로 동작하게 함으로써 병렬처리를 촉진한다.
또한 이벤트 메시지를 사용하면 발신자와 수신자를 장소와 시간에서 쉽게 분리할 수 있다.
이벤트 주도 아키텍처는 이러한 느슨한 결합으로 인해 확장성 및 수정 가능성에 많은 이점을 제공한다.
토폴로지란 컴퓨터 네트워크의 요소들(링크, 노드 등)을 물리적으로 연결해 놓은 것, 또는 그 연결 방식을 말한다.
이벤트 주도 아키텍처에서는 주로 Broker Topology
방식과 Mediator Topology
방식으로 토폴로지를 구성한다.
이벤트의 흐름을 관리하는 별도의 중재자가 존재하는 방식이다.
여러 단계가 있고 순서가 연속적이며 병렬 처리를 위해 오케스트레이션이 필요한 경우 적합하다. 예를들어 주식 거래를 하는 단일 이벤트는 먼저 거래를 확인한 다음 다양한 준수 규칙에 따라 해당 주식 거래의 준수 여부를 확인하고, 브로커에게 거래를 할당하고, 수수료를 계산하고, 마지막으로 그 브로커에게 전달한다.
중재자 토폴로지 방식에서는 두가지 유형의 이벤트(최초 이벤트, 처리단계 이벤트)가 존재한다.
이벤트 큐, 이벤트 중재자, 이벤트 채널, 이벤트 프로세서 컴포넌트로 구성된다.
중재자는 Spring Integration
, Apache Camel
등을 통해 구현할 수 있다.
일반적으로 이벤트 프로세서 구성요소들은 단일 비즈니스 태스크를 수행해야하며 특정 이벤트를 완료하기 위해 다른 이벤트 프로세서에 의존하지 않아야한다.
브로커 토폴로지 방식은 이벤트의 흐름을 관리하는 별도의 중재자가 존재하지 않는 방식이다.
이벤트의 흐름은 경량 메시지 브로커(예: Kafka, RabbitMQ 등)를 통해 체인과 같은 방식으로 이벤트 프로세스 구성 요소에 분산한다.
이 토폴로지는 비교적 간단한 이벤트 처리 플로우가 있고 중앙 이벤트 오케스트레이션이 필요하지 않은 경우에 유용하다.
이벤트 주도 아키텍처
로 전반적인 아키텍처를 구성할 경우, Event Sourcing
에 대해 이해하고 있어야한다. 기존의 우리에게 익숙한 CRUD
는 항상 이벤트가 발생하면 그 결과값만을 저장소에 저장하고 꺼내 보여주었다.
이벤트 소싱이란 이벤트가 발생하면 그 이벤트의 결과를 저장하는 것이 아니라 이벤트 자체 하나하나를 저장하는 방식 이다.
이벤트 스트리밍 중에 장애가 발생한다면? 해당 지점부터 이벤트를 다시 재생하는 방법으로 failover이 가능하다.
이벤트 소싱을 구현하려면 그 이벤트가 저장되는 이벤트 저장소
가 있어야 한다.
commit
혹은 rollback 되어야 한다commit
된 이벤트는 유실되어서는 안된다event
중 aggregate
별로 데이터를 읽을 수 있어야 한다이러한 조건을 만족하는 이벤트 저장소를 구현해주는 솔루션이 존재한다.
이벤트 스토어: https://www.eventstore.com/
라곰: https://www.lightbend.com/lagom-framework/
액손: https://www.axonframework.org/
이벤추에이트: https://eventuate.io/
꼭 해당 솔루션을 쓸 필요는 없다. 배달의민족의 경우 RDB로 이벤트 저장소를 구현하였다고 한다. (참고링크: https://techblog.woowahan.com/7835/)
발생한 모든 이벤트를 언제까지 가지고 있을 것인가.. 평생 가져야하나.... 또한 결과값을 보려면 매번 처음부터 이벤트를 재생해주어야 한다.
이벤트 재생을 하는데 재생해야할 이벤트가 많아지면 많아질수록 read 부하가 발생할 것이다.
부하 발생에 대해 두가지 방법으로 해결가능하다.
Event Store에서 수행하는 동작이다.
Snapshot
을 찍을경우 Event Projection
을 수행하여 현재의 상태 정보를 저장한다.
이후에 Event Projection
수행시 모든 Event
를 대상으로 Projection
을 수행하는 것이 아니라 Snapshot
과 Snapshot
이후의 Event
만을 Projection
하여 Read
동작의 부하를 줄인다.
CQRS 패턴
이란 쉽게 말해서 CRUD
동작 중 CUD
와 R
에 대한 책임을 분리하는 디자인 패턴이다.
CUD
에 해당하는 데이터베이스와 R
에 해당하는 데이터베이스를 분리한다면? 발생하는 CUD
이벤트에 대해 이벤트 브로커를 통해 R
쪽 데이터베이스에 반영해준다면 이벤트 저장소에는 CUD에 대한 이벤트가, Read Database에는 결과값이 저장될 것이고 조회 시마다 read 부하는 발생하지 않을 것이다.
https://www.oreilly.com/library/view/software-architecture-patterns/9781491971437/ch02.html