이번 포스팅은 저번 포스팅에서 언급한 것처럼 두 번째 주제인 Event Driven Sysmtem에 대해 정리해보려고 합니다.
- Spring Reactor - 동기 vs 비동기, 블로킹 I/O와 논블로킹 I/O
- Event Driven System
다만 이번 주제의 내용에서 다룰 내용이 많고 복잡해서 포스팅을 세 개로 나눠서 작성하려고 합니다.
- Event Driven System
- Event Driven System 이란?
- CQRS 패턴
- 메시징 시스템 - Kafka
이번 포스팅은 Event에 대해 공부한 내용을 작성해보려고 합니다. 물론 Event에 대한 내용에 대해 더 알고자 교재 말고도 Designing Event-Driven Systems-Concepts and Patterns for Streaming Services with Apache Kafka 해당 레퍼런스를 참고했음을 미리 말씀드립니다.
7장의 내용은 이벤트 기반 시스템에 대한 전반적인 내용을 알아야만 이해할 수 있다고 판단되었고 때문에 책에서 설명이 부족한 부분들을 추가로 찾아서 학습한 내용들을 공유 할 예정입니다.
Event Driven System 이란?
이벤트는 단순히 특정 상황이나, 사실이 발생했음을 알리는 것에 불과합니다. 중요한 건 이벤트라는 개념을 사용해서 어떻게 전체 시스템을 느슨하게 결합하며 장애에 유연하게 대처할 수 있는지가 중요한 것 같습니다.
하나의 예시를 통해 이벤트 기반 시스템 적용 전후가 어떻게 다른지 보려고 합니다.
예를 들어, 마이크로서비스나 SOA(Service Oriented Architecture) 서비스 지향형 아키텍처에서 이벤트 기반 시스템의 적용을 하기 전에는 사용자의 주문을 처리하는 로직이 다음과 같다고 해봅시다.
먼저 사용자의 '구매' 버튼 클릭과 함께 주문 생성 요청이 주문 서비스로 들어옵니다. 주문 서비스에서 주문의 결제정보를 확인하고 주문을 최종 생성한 이후에는 해당 주문을 배송 준비 중 상태로 전환하며 배송 서비스로 해당 주문의 배송 처리 요청을 보냅니다.
배송 서비스는 주문에 대한 사용자의 주소, 수령자, 휴대폰번호등 배송에 필요한 정보를 사용자 서비스로 요청을 보내고 응답을 받습니다. 그리고 배송서비스는 사용자 서비스의 응답을 기반으로 배송을 위한 일련의 과정을 처리합니다.
이 설계의 가장 큰 특징은 서비스 간 존재 여부를 알 고 있다는 점입니다. 주문 서비스가 배송 서비스를, 배송 서비스가 사용자 서비스를 알 고 요청을 보내는 것처럼 이 설계에서는 서비스 간에 타 서비스의 존재 여부를 알 고 있어야 한다는 점입니다.
즉, '느슨한 결합'이 되지 않았다는 점입니다. 느슨한 결합이란 각 서비스들의 변경이 타 서비스들에게 영향을 미치지 않다는 점인데 현재로서는 배송서비스의 변경이 발생한다면 주문 서비스로 영향을 줄 수 있습니다. 만약 배송 서비스에 장애가 발생한다면 주문 서비스까지 장애가 발생할 수 있다는 점에서는 위 설계는 약점이 존재한다고 할 수 있습니다. (하지만, 항상 느슨한 결합만이 정답인가? 이에 대한 답은 아래에서 한번 언급하려고 합니다.)
이번에는 이벤트 기반 시스템에서 주문을 처리하는 방법에 대해 보려고 합니다. 위 설계와 가장 큰 차이점은 Kakfa라는 메시징 시스템의 존재 여부와 각 서비스로의 작업 호출이 HTTP나 RPC 같은 프로토콜에 의한 호출이 아닌 오직 이벤트에 의해서만 커뮤니케이션한다는 점입니다.
위 설계에서는 주문 생성 요청이 주문 서비스로 들어옵니다. 주문 서비스는 이전과 동일하게 주문을 처리하고 해당 주문이 정상적으로 생성됐음을 배송 서비스에 전달하지 않습니다. 주문 서비스는 카프카로 해당 주문이 생성되었다는 이벤트를 발생시킵니다. 카프카는 해당 이벤트가 발생하면, 배송 서비스로 해당 주문의 배송 처리가 필요함을 알립니다. (주문 서비스가 이벤트를 발생시키고 카프카가 어떻게 이벤트를 어떻게 수신하며 다른 서비스로 이벤트를 전송하는지에 대한 과정은 다른 포스팅에서 정리해보려고 합니다.)
위 설계에서 주문 서비스와 배송 서비스 간의 상호 작은 직접적으로 서로 호출하는 대신 이벤트를 통신한다는 점을 제외하면 별로 바뀐 게 없어 보입니다. 다만, 중요한 변화는 주문 서비스가 배송서비스의 존재를 알지 못한다는 점입니다. 주문 서비스는 주문이 생성되었음을 알리는 이벤트를 발생시키기만 할 뿐 이제 배송 서비스가 이 상호작용에 참여할지 여부를 직접 결정합니다. 즉, 이전 설계에서는 배송 서비스가 아닌 주문 서비스가 배송 서비스의 상호작용 참여를 강제하고 있지만 이벤트 기반 시스템에서는 배송 서비스가 상호작용 참여를 직접 결정합니다. 이는 receiver-driven routing(수신자 기반 라우팅) 설계의 예로 이벤트의 라우팅 작업이 발신자가 아니라 수신자에 위치하게 됨을 의미합니다.
정리하자면 주문 서비스와 배송 서비스의 결합도를 낮출 수 있게 되었습니다. 이렇게 되면 배송 서비스의 장애가 발생해도 주문 서비스는 별다른 영향을 받지 못합니다. 굳이 따지자면 해당 주문의 배송이 늦어지는 것뿐이겠죠. 반대로 주문 서비스의 장애는 배송 서비스로 그 어떤 영향도 줄 수 없는 느슨한 결합이 가능해졌습니다.
이벤트 기반 시스템은 시스템에 유용한 기능들을 추가하려고 할 때에도 강력한 힘을 발휘합니다. 예를 들어 시스템에 상품 가격을 수요와 공급에 따라 업데이트하는 서비스를 추가하기로 결정했다고 가정해 봅시다. REST 기반의 시스템에서는 주문 서비스와 결제 서비스에 새로운 메서드(가격 업데이트를 위한 메서드)를 추가해서 기존 코드를 변경하고 다시 배포해야 하는 번거로움이 있습니다.
# 주문 서비스 코드 예시
def repricingProdcut(order):
price = get_price_from_repricing_service(order.product_id)
update_order_price(order, price)
이벤트 기반 시스템에서는 단순히 Repricing 서비스를 만들고 기존의 이벤트 스트림(kafka 와 같은)에서 주문, 결제 이벤트를 구독하여 가격 업데이트를 하는 Consumer를 만듦으로써 기능을 구현할 수 있습니다. 즉, 기존의 서비스의 수정 배포 없이 동일한 주문, 결제 이벤트를 사용해 새로운 기능을 추가하는 것이 가능해지는 것입니다.
# 리프라이싱 서비스 코드 예시
def handle_event(event):
if event.type == 'OrderCreated':
price = calculate_price_based_on_supply_demand(event.product_id)
send_price_update(event.order_id, price)
이처럼, 이벤트 기반 시스템은 위 예시처럼 이벤트를 기반으로 각 서비스나 컴포넌트가 서로 상호작용 하게 함으로써 응집도를 높이고 결합도를 낮추며, 새로운 기능의 추가를 용이하게 하는 설계를 의미합니다.
다음 주제로 넘어가기전 이전에 언급한 느슨한 결합이 항상 옳은 것인가? 에 대해 생각해 봅시다.
일반적으로 느슨한 결합은 각 서비스나 컴포넌트의 독립적 수정, 배포가 가능하다는 점, 장애를 예방할 수 있다는 점 등에서 좋다는 의견이 대다수입니다. 느슨하지 않은 결합은 다른 말로 '공유' 하고 있는 부분이 많다는 것을 의미하고 느슨한 결합에 비해 더 강하게 협력하고 있다고 볼 수 있습니다. 다만 느슨한 결합을 설계하는 가장 큰 문제는 비용이라고 생각합니다. 소규모 프로젝트를 진행하면서 슨한 결합을 위해 이벤트 기반 시스템과 같은 고비용 설계를 하는 것보다 모놀리틱 한 아키텍처를 설계함으로써 빠르게 배포되는 게 중요한 경우에는 오히려 비용 낭비라고 생각합니다. 따라서 각 상황에 맞게 방법을 선택하는 것이 중요하다고 생각합니다.
다음 포스팅에서는 메시징 시스템 중에서도 카프카란 무엇이며, 메시징 시스템의 핵심 개념들과 카프카의 동작 원리에 대해 정리해보려고 합니다.
'도서 > 스프링으로하는 마이크로서비스 구축' 카테고리의 다른 글
Spring Cloud Eureka (0) | 2024.07.25 |
---|---|
Messaging System - Apache kafka (2) | 2024.07.12 |
Spring Reactor - 동기 vs 비동기, 블로킹 I/O vs 논블로킹 I/O (0) | 2024.06.29 |
동시성 문제와 해결 (0) | 2024.06.16 |
Spring REST Docs, API 문서화 (0) | 2024.06.10 |