Event Driven Architecture
Last updated
Last updated
참고 링크
Event Driven 은 programming, Architecture 등과 연결되어 다양한 정의로 표현된다.
컴퓨터 회로를 구동시키기 위해 발생하는 일 (마우스 클릭, 키보드 타이핑, 모바일 터치) 등
특정 행동이 자동으로 순서에 따라 발생하는 것이 아닌 일에 대한 반응으로 동작하는 디자인 패턴
시스템 내 외부에 발생한 주목할 만한 상태의 변화에 기반한 동작
등등 ..
Event Driven MicroService(EDM) 은 MSA 가 적용된 시스템에서 이벤트 발생 시 해당 이벤트 로그를 보관하고 이를 기반으로 동작하며, 비동기 통신을 통해 시스템 내 통합(integration) 을 수행하는 Architecture 이다.
이벤트
IT 영역에서 이벤트는 다양한 정의를 갖지만, 이 곳에서 언급하는 이벤트는 상태의 변경. 즉 데이터의 변경, 생성, 삭제를 통해 발생하는 서비스를 의미한다.
이벤트 로그 보관
현재의 데이터는 상태 변경의 누적이라는 아이디어에서 시작한다. 이 때 상태 변경은 이벤트를 뜻하고, 이를 누적하는 행위는 이벤트 로그를 보관하는 것이다. EDM 에서 생성된 이벤트는 반드시 보관되어야 한다. 이는 현재 상태를 구성하는 근간이 된다. 또한 보관된 이벤트를 바탕으로 장애 발생 또는 특정 요구사항에 따라 지점된 시점으로 복원을 수행한다.
이벤트 로그를 보관하는 장소를 이벤트 스토어라 칭한다.
비동기 통신
메세징 프로토콜을 통한 메세지 큐 방식이 자주 사용된다.
서비스에서 데이터의 생성, 변경, 삭제를 통해 이벤트가 발생하면 발행 서비스는 메세지 형태로 이벤트를 발행하고, 해당 이벤트에 관심이 있는 서비스에서 구독을 수행한다.
시스템 내 통합(integration)
이상적으로 구현된 MSA 는 서비스간 데이터 참조를 위한 내부 통신이 필요없지만, 현실적으로 서비스 간 내부 통신이 전혀 없는 시스템을 구현하기란 불가능에 가깝다.
다양한 사유로 여러 서비스 간 통신을 통해 연동이 발생한다.
이벤트를 데이터의 변경, 생성, 삭제로 정의했기 때문에 MSA 의 데이터 관리와 밀접한 연관성을 갖는다. 데이터는 현재의 상태를 나타내고 이는 보관된 데이터 변경, 생성, 삭제 기록 즉 이벤트 로그에 기반한다.
특정 서비스에서 기능을 수행한다.
이벤트가 발생(데이터의 생성, 변경, 삭제) 하면 해당 도메인 객체를 기반으로 이벤트를 생성한다.
생성된 이벤트는 저장 공간에 보관되고 비동기 메세지 큐로 해당 이벤트에 관심이 있는 서비스들에게 전달된다.
이벤트를 구독한 서비스는 해당 이벤트를 처리한다.
수행 도중 오류가 발생하면 저장된 이벤트 로그를 기반이로 retry/rollback 을 수행한다.
MSA 는 나뉘어진 서비스와 서비스 별 각자의 데이터베이스 구성을 지향한다. 이로 인해 발생하는 새로운 구조적 요구사항들이 있다. EDM 을 적용해 새로운 요구사항들을 충족시킬 수 있다.
서비스 별 각자 데이터베이스를 적용한 시스템에서 데이터 무결성을 보장할 수 없다. 하지만, EDM 을 통해 최종적인 데이터의 일관성을 유지할 수 있다.
예를 들어, A 서비스에서 데이터가 변경된 것을 B 서비스에서도 바로 알기 위해서는 중앙에서 모든 데이터 변경을 통제해야 하는데, MSA 구조에서는 이런 중앙화 된 트랜잭션이 없습니다.
여기서 주의할 점은, A 서비스의 변경사항이 B 서비스에 영향을 주는 경우에만 데이터가 전파되는 것이 의미가 있다.
MSA(MicroServer Architecture) 가 Event Driven 과 연결되어 언급되는 이유는 무엇일까?
MSA 를 도입하여 느슨한 결합, 관심사의 집중, 단일 책임 원칙, 빠른 배포주기, 폴리글랏(?), calability, 장애의 격리, 유연성, 확장성 등 여러 이점을 확보할 수 있습니다.
하지만 이를 위해서 넘어야 하는 허들이 있다.
MSA 를 적용한 시스템은 서비스가 쪼개지고 데이터베이스가 쪼개진다. MSA 에서 주요 원칙 중 하나는 서비스 별 자체 로직과 데이터, 그에 따른 최적의 DB 를 선택하는 것이다.
이상적으로 하나의 기능을 수행할 때 자신의 서비스 내에서 모두 해결할 수 있도록 분리를 잘한다면 시스템 내 통합은 고려하지 않아도 된다.
하지만 현실적으로 서비스 분리에 따른 서비스 간 데이터 참조 등 시스템 내부에 연결이 발생한다. 또한, 서비스가 뭉쳐져 있을 때 발휘되던 장점을 활용하지 못하는 경우도 발생한다.
Database Per Service 는 MSA 의 느슨한 결합, 관심사의 집중, 폴리글랏(?) 프로그래밍, 독립적인 배포 주기 등을 달성하기 위한 핵심 키워드이다.
하지만 Database Per Service 는 가장 어려운 부분 중 하나이다.
DB 변경 / 분리 시 고려해야 하는 요소들은 다음과 같다.
엔터프라이즈에서 데이터가 가장 중요한 자산
금융 데이터, 통신 사용량 등 돈과 관련한 데이터
데이터를 기반으로 사용자 인사이트 확보 -> 돈, 매출의 중요한 기반 요소 (데이터 자체가 돈이 된다)
기업 리더 중 90% 가 토지, 인재, 자본 등과 마찬가지로 데이터도 가장 중요한 리소스인 동시에 가장 기본적인 차별화 요소로 판단
데이터 기반 경제 -> 의사결정의 기반
Shared 관계형 데이터베이스 장점 사용 불가
데이터를 효율적으로 보관하고 조회 / 삭제 등 기능의 효율이 높이는 장점
테이블 조인을 통한 통합 뷰 제공
트랜젝션 가능 ..
database oriented system
모든 업무의 정의는 DB 스키마 / 테이블 설계부터 시작
결국 세스템의 성격은 데이터의 CRUD 기능, 데이터의 흐름과 life cycle 이 중요
DB 분리는 결국 기존 데이터의 흐름을 깨는 행위 ..
데이터베이스 분리시 발생하는 비용
기존 DBMS 에 최적화된 각종 세팅
기타 등등 ..
위와 같은 이유로 Database Per Service 를 적용하는 것은 어렵다. 하지만 반대로 중앙화된 Shared Database 를 사용하는 것은 많은 제약사항이 있다.
중앙화 된 Shared Database DB 는 시스템의 응집력을 저해하고 종속성을 높인다.
단일 트랜잭션 처리에 따라 테이블 락 등 장애 발생 가능성이 있다.
중앙화 된 DB 에 장애 발생 시 전체 시스템 장애를 일으킨다.
DB 스케일링이 어렵다.
서비스 특징에 따른 최적의 DB 선택이 어렵다.
기존의 관계형 데이터베이스 구성에서 MSA 의 이점을 살리기 어려운 이유는 명확하다.
하지만, 기존의 관계형 데이터베이스 구성, 그 중 관계형 DB 를 사용하는 경우, 트랜잭션 처리, 데이터 무결성, Join 사용, 관계를 활용한 데이터 저장 / 조회 등 DBMS 레벨에서 여러 유용한 기능을 사용했었다.
Database Per Serivce 를 적용하면 관계형 DB 레벨에서 제공하는 기능을 Application 레벨에서 해결해야 한다.
Database Per Service 적용의 기술적으로 어려운 부분을 해결하는데 Event Driven 이 효율적으로 적용된다.
모바일, SNS, IOT 등 다양한 시스템이 발전하고 있는 상황에서 이들 시스템이 다루는 데이터는 스트림 형태의 비정형 데이터가 많다.
매우 빠른 읽기/쓰기 성능을 지원해야 하고 분산형 구조를 통해 데이터를 여러 대의 서버에 저장한다. 상호 복제해 특정 서버에 장애가 발생했을 때에도 데이터 유실이나 서비스 중지가 없는 형태의 구조 등 기존 관계형 DB 에 최적화 되지 않은 기능을 요구한다.
확장성, 단순한 구조, 낮은 비용, 빠른 검색 등의 NoSQL DB 가 적합하다.
이런 경우 MSA 의 Database Per Service 를 적용해 각 서비스 목적에 맞는 최적의 DB 를 선택할 수 있다.
기존 시스템에서 서로 다른 DB 를 사용하는 경우 두 DB 간 트랜잭션 처리가 어려웠다. 하지만 MSA 에서는 다른 서비스의 데이터를 참조할 때 직접 접근하지 않고 데이터의 캡슐화를 통해 API 를 통해서만 접근할 수 있도록 한다.
DBMS 레벨이 아닌 Application 레벨에서 트랜잭션 처리를 수행하는데 적합해 서비스의 목적에 최적화 된 DB 를 선택하는데 도움을 준다.
MicroService 에 Event Dreiven 을 엮으면 MSA 를 도입하면서 새로 발생한 요구 사항(허들) 을 달성할 수 있다 .
비지니스 흐름에 따른 로직 수행
분산 트랜잭션 처리
서비스 간 반정규화 데이터 동기 처리
적절한 시스템 통합
최종적인 일관성
핫초코를 구매하는 과정을 생각해보자.
메뉴 선택
카운터에서 핫초코 주문 -> 상태 변화
계산 -> 상태 변화
핫초코 만들기 -> 상태 변화
핫초코 먹기 -> 상태 변화
핫초코 구매라는 하나의 기능이 여러 단계에 걸쳐서 수행된다. 각 단계마다 상태 변화를 동반한다. 이전 단계를 완료하면 그에 반응해서 다음 단계를 수행한다.
MSA 에서 각 단계별로 서비스를 구현했다면 menu -> order -> pay -> make -> delivery 서비스 순서대로 동작할 것이다. 이 연결 관계에서 비지니스 흐름을 파악하는 것은 기능 수행을 위해서 매우 중요하다 .
이 일련의 과정에 EDM 을 적용해 상태가 변경되면 이벤트를 발생시키고, 이를 관심 있는 서비스가 수신 후 기능을 수행하면서 비지니스 흐름에 따라 각 서비스의 기능을 수행할 수 있다.
오류가 발생하는 상황을 가정해보자
메뉴 선택
카운터에서 핫초코 주문 -> 상태 변화
계산 -> 상태 변화
핫초코 재고 부족 -> 오류
환불, 주문 폐기 -> rollback
비지니스 흐름에 따라 기능을 수행하다가 중간에 문제가 발생할 수 있다. 문제가 발생한 시점에 rollback 또는 retry 를 수행한다.
기존의 레거시 시스템에서 문제 발생시 일관된 commit 또는 rollback 처리나 이전에 발생한 상태 변경에 직접 접근해서 데이터 수정이 가능하다. 하지만 MSA 가 적용된 시스템에서 서로다른 서비스에 걸쳐진 기능을 수행하는 도중 일관된 commit 또는 rollback 을 수행할 수 없다. 이 때 EDM 을 적용해 rollback 또는 retry 를 처리할 수 있다.
rollback이 필요한 경우 Failed 이벤트를 발생시키고, 이를 이전 스텝을 수행한 서비스에서 구독하여 보관되어 있던 이벤트 로그 기반으로 rollback을 수행합니다. retry가 필요한 경우 메세지 큐의 requeue 또는 dead letter queue 기능을 사용해 retry 처리를 수행할 수 있습니다.
MSA 에서 내부 통신은 크게 2가지가 사용된다. 두가지 방식 모두 강점과 약점이 있다. 상황에 따라서 적합한 방식을 사용하면 된다.
REST 통신
메세지 큐 통신
REST 통신은 실시간으로 보여줘야 하는 데이터를 모으는 등 조회 기능을 수행하기에 적합하다. 하지만 모든 내부 통신을 REST 로 수행할 경우 몇가지 문제점이 발생할 수 있다.
서비스 간 의존성
여러 서비스간 데이터 생성, 삭제, 변경이 얽혀있다면 어느 서비스에서 Client 의 응답을 받아서 누구에게 전달해 처리해야 하는지 모호하다.
서비스가 추가된다면 해당 기능 수행에 연관있는 모든 어플리케이션에 수정이 필요할 수 있다. (결합도가 높아진다..)
데이터 관리 매우 어려움
서비스 간 분산 트랜잭션 처리 및 중간에 오류가 발생했을 때 복원 시점, 복원 키 값을 찾는 등에 어려움이 있다.
서비스 간 반정규화 된 동일한 데이터를 변경할 때 요청 받은 서비스가 반정규화된 데이터가 위한 서비스를 모두 찾아서 변경하도록 처리해야 한다.
데이터 오너 서비스가 반정규화 해간 서비스 리스트를 관리하지 않는 이상 그에 따른 데이터의 일관성을 유지하기가 어렵다.
동기 통신의 비용
외부에서 API 를 통해 요청이 전달되었을 때, 응답을 하기까지 연관된 모든 서비스의 자원을 홀딩한다.
비동기 메세지 큐 방식은 시스템 내 통합에 적합하다.
서비스 간 결합도가 낮아진다.
서비스 흐름이 단순해진다.
발행 서비스는 구독 서비스들을 고려할 필요 없이 데이터 생성, 변경, 삭제가 발생하면 이벤트를 발생시킨다.
구독 서비스는 메세지 큐의 라우팅 룰에 따라 전달되는 이벤트를 구독해 자신만의 비지니스 로직을 수행한다.
응답 지연시간이 줄어든다.
실시간으로 처리 및 전달해야 하는 기능을 먼저 수행하고 나머지 후속동작은 메세지 큐를 통하게 적용한다면 응답 지연 시간을 낮출 수 있다.
... (여기서부터는모르는 내용이다..)