티스토리 뷰

Architecture

Event Driven Architecture

realizers 2023. 9. 16. 17:17
728x90
반응형

By unsplash

 

Event Driven Architecture


이벤트 기반 아키텍처는 확장성이 뛰어난 고성능 애플리케이션 개발에 널리 쓰이는 비동기 분산 아키텍처입니다. 분산 시스템에서 이벤트를 발행하고 수신자에게 이벤트를 전송하는 구조로 수신자는 해당 이벤트를 처리하는 방식의 아키텍처입니다. 

 

토폴리지


이벤트 기반 아키텍처의 다양한 하위 패턴을 알아보기 위해서는 토폴리지를 이해하는 것이 중요합니다. 이벤트 기반 아키텍처는 중재자 토폴리지, 브로커 토폴리지 기반으로 합니다.

 

브로커 토폴리지


브로커 토폴리지에서 메시지는 메시지 브로커(RabbitMQ, Kafka)를 통해 브로드캐스팅되는 식으로 이벤트 프로세스 컴포넌트에게 분산되어 흘러갑니다. 브로커 토폴리지는 이벤트 처리 흐름이 단순하고, 중앙에서 이벤트를 조율할 필요성일 없을 때 유용합니다.

 

브로커 토폴리지는 네 가지 기본 아키텍처 컴포넌트를 가집니다. 

 

시작 이벤트

 

  • 시작 이벤트는 단순한 이벤트든 복잡한 이벤트든 전체 이벤트 흐름을 개시하는 이벤트를 말합니다.
  • 시작 이벤트는 이벤트 브로커의 채널로 전송됩니다.

이벤트 브로커

 

  • 시작 이벤트 또는 가공된 이벤트를 보관하고 있는 메시지 브로커입니다.(RabbitMQ, Kafka)
  • 이벤트 브로커를 subscribe하고 있는 이벤트 프로세서에게 메시지를 발행합니다.

이벤트 프로세스

 

  • 이벤트 브로커로부터 메시지를 가져와 자신의 역할에 맞는 임무를 수행합니다. 
  • 자신이 맡은 임무를 수행한 뒤 처리 이벤트를 발행하여 시스템의 나머지 부분에 자신이 한 일을 비동기로 알립니다. 

처리 이벤트

 

  • 다른 이벤트 프로세스 처리 이벤트 메시지 브로커를 구독하고 있다가 이벤트가 들어오면 그에 맞는 작업을 수행한 뒤 새로운 처리 이벤트를 발행합니다. 

대략적인 구조

 

브로커 토폴로지에서는 다른 이벤트 프로세스의 관심 여부와 무관하게 각 이벤트 프로세스가 자신이 한 일을 모두에게 알리는 게 항상 바람직하다고 합니다. 그래야 나중에 이벤트를 처리하는 과정에서 기능추가가 필요하게 되더라도 아키텍처를 쉽게 확장할 수 있다고 합니다. 

 

💡 예시

 

아래 예제 그림을 보면 각 서비스는 분리되어 있고, 독립적으로 움직입니다. 브로커 토폴리지는 마치 릴레이 경주와 같다고 생각하면 이해하기 쉽습니다. 각 주자는 이벤트라는 바통을 들고 자신의 역할에 맡는 임무를 다합니다. (500m 달리기) 그리고 임무를 다하였으면 다음 주자에게 바통을 넘겨주는 식으로 마지막 주자가 결승전을 통과할 때까지 정해진 순서에 따라 진행됩니다. 그리고 경주를 마친 주자는 다음 질주를 위해 대기하게 됩니다. 

브로커 토폴로지도 이와 비슷하게 동작을 하는데 각 서비스는 처리 이벤트를 발행 후 해당 이벤트에 더 이상 관여하지 않고 다른 이벤트에 반응할 준비를 합니다. 

 

 

💡 브로커 토폴로지의 장단점

 

장점

 

  • 모든 이벤트 프로세스의 커플링이 낮아집니다.
  • 확장성이 높습니다.

단점

 

  • 시작 이벤트와 연관된 전체 워크플로를 제어할 수 없습니다. 따라서 각 서비스에서 실제 트랜잭션이 언제 끝났는지 알 수 없습니다.
  • 에러 처리가 어렵습니다. 비즈니스 트랜잭션을 관리/통제하는 중재자가 없으므로 처리를 실패해도 다른 서비스에서는 해당 사실을 알 수 없습니다. 
  • 비지니스 트랜잭션을 재시작하는 기능도 브로커 토폴로지에서는 지원되지 않습니다. 처음 시작 이벤트를 처리할 때부터 이미 다른 작업이 비동기로 수행된 터라 시작 이벤트를 다시 넣는 것은 불가능 합니다. 

 

중재자 토폴리지


중재자 토폴리지는 위에서 살펴본 브로커 토폴리지의 단점을 일부 보완할 수 있습니다. 여러 이벤트 프로세스 간의 조율이 필요한 시작 이벤트에 대해 워크플로를 관리/제어하는 이벤트 중재자가 핵심입니다.

 

중재자 토폴리지는 다섯가지 기본 아키텍처 컴포넌트를 가집니다. 

 

시작 이벤트

 

  • 시작 이벤트는 단순한 이벤트든 복잡한 이벤트든 전체 이벤트 흐름을 개시하는 이벤트를 말합니다.
  • 시작 이벤트는 이벤트 브로커의 채널로 전송됩니다.

이벤트 중재자

 

  • 이벤트 중재자는 이벤트 처리에 관한 단계 정보만 가지고 있으므로 점대점 메시징으로 각각 이벤트 채널로 전달되는 이벤트를 생성합니다. 

이벤트 채널

 

  • 이벤트 중재자와 이벤트 프로세스가 메시지를 주고 받기 위한 의사소통 창구입니다.
  • 이벤트 중재자는 이벤트 채널을 통해 이벤트 프로세스에게 이벤트를 전달하는 창구입니다.
  • 이벤트 프로세스는 자신 임무를 완료했다는 것을 알리기 위해 이벤트 채널을 통해 이벤트 중재자에게 메시지를 전달하는 창구입니다.

이벤트 프로세스

 

  • 이벤트 프로세스는 이벤트 채널로부터 이벤트를 받아 처리한 다음 이벤트 채널로 작업을 완료했다는 응답을 보냅니다.

 

💡 일반적인 아키텍처

 

아래 그림은 가장 일반적인? 중재가 토폴로지 입니다. 이벤트 중재자는 처리하는 이벤트의 특성과 복잡도에 따라 다양하게 구현할 수 있습니다. 

 

 

💡 복잡도에 따른 아키텍처

 

이벤트 복잡도를 한 가지 기준으로 평가하는 경우는 거의 없으므로 알기 쉽게 단순함, 어려움, 복잡함 정보로 분류한 뒤 모든 이벤트가 항상 단순한 중개자를 거치도록 하는 것이 좋습니다.

 

 

💡 예시

 

아래 예시는 위의 브로커 토폴리지에서 사용된 예시입니다.

1단계에서는 접수를 생성하기 위해 이벤트 중재자는 접수 서비스의 이벤트 채널에 이벤트를 발행하고, 접수 서비스는 임무를 완료한 뒤 이벤트 중재자에게 이 사실을 알립니다. 그리고 이벤트 중재자는 응답을 받아 2단계로 넘어가게 됩니다.

1단계

 

2단계에서는 결제에게 이벤트를 보내고 응답을 받으면 3단계로 넘어가게 됩니다. 

2단계

 

3단계에서는 똑같이 이벤트 중재자가 알림과 라이더 이벤트를 발행하고 각 서비스는 이에 대해 임무를 완료한 뒤 이벤트 중재자에게 응답을 하게됩니다.

3단계

💡 중재자 토폴로지의 장단점

 

장점

 

  • 이벤트 중재자는 브로커 토폴로지와는 다르게 전체 워크플로에 대해 잘 알고 있고 통제가 가능합니다. 따라서 이벤트 상태에 대해 에러처리, 복구, 재시작을 할 수 있습니다.

단점

 

  • 이벤트 프로세스는 브로커 토폴리지와 마찬가지로 확장할 수 있지만, 이벤트 중재자도 함께 확장해야 하므로 이벤트 처리 흐름에 병목 지점이 생길 수 있습니다.
  • 이벤트 중재자는 이벤트 처리를 중간에서 제어해야 하므로 이벤트 프로세스가 상대적으로 더 많이 커플링되어 성능은 브로커 토폴리지에 비해 좋지 않습니다.

 

💡 정리

 

브로커 토폴리지냐, 중재자 토폴리지냐, 결국 워크플로 제어와 에러처리 기능이 우선인가 아니면 고성능과 확장성이 더 우선인가에 대한 트레이드오프를 해야합니다.

 

 

에러처리


리액티브 아키텍처의 워크플로 이벤트 패턴은 비동기 워크플로에서 에러처리 문제를 해결하는 한 가지 방법입니다. 워크플로 이벤트 패턴은 워크플로 대리자를 통해 위임, 봉쇄, 수리 작업을 합니다. 

 

이벤트 프로세스는 이벤트 채널을 통해 데이터를 이벤트 컨슈머에게 비동기로 전송하고, 이벤트 컨슈머가 데이터를 처리하는 도중 에라가 발생하면 즉시 에러를 워크플로 프로세스에게 위임한 뒤 이벤트 채널에 있는 다음 메시지로 넘어갑니다. 

이렇게 에러가 발생하더라도 다음 메시지를 처리할 수 있으므로 전체 응답성은 영향을 받지 않습니다. 

 

에러를 수신한 워크플로 프로세스는 메시지의 잘못된 내용을 파악한 뒤 원데이터를 변경해서 긴급 조치한 뒤 원래 큐로 돌려 보냅니다. 그리고 이벤트 컨슈머는 이 메시지를 새로운 메시지로 간주하여 재처리를 하게 됩니다. 하지만 다시 에러가 발생하면 다시 워크플로 프로세스가 받게되고 이번에는 문제를 파악할 수 없게된다면 대시보드라고 부르는 애플리케이션(이메일)으로 보내고 업무 담당가 처리할 수 있도록 합니다.

 

 

 

데이터 소실 방지


비동기 통신을 할 때 데이터 소실은 언제나 중요한 관심사입니다. 하지만 이벤트 기반 아키텍처는 데이터가 소실될 만한 곳이 많습니다. 여기서 데이터 소실이란 메시지가 도중에 삭제되거나 최종 목적지에 도착하지 못한 상태를 말합니다.

 

데이터 소실 지점

소실 1 : 메시지가 큐로 전달되지 않거나 전달되어도 다음 프로세스가 메시지를 가져가기전에 브로커가 다운되는 경우

 

  • 동기 전송과 퍼시스턴트 메시지 큐를 이용해서 해결할 수 있다고 합니다.

소실 2 : 프로세스 B가 큐에서 메시지를 꺼내 이벤트를 처리하기 전에 장애가 발생하는 경우

 

  • 클라이언트 확인응답 모드라는 메시징 기술을 사용해서 해결가능 합니다. 원래 메시지는 큐에서 빠져나가는 즉시 삭제되는데(자동 확인응답 모드), 클라이언트의 확인응답 모드는 메시지를 큐에 보관한 채 다른 컨슈머가 메시지를 읽을 수 없게 클라이언트 ID를 메시지에 부착합니다. 따라서 프로세스 B가 잘못되어도 메시지는 큐에 남아있으므로 해결할 수 있습니다.

소실 3 : 데이터 에러로 인해 데이터베이스에 저장할 수 없는 경우

 

  • 최종 참여자 지원을 사용하여 메시지 처리가 끝나 데이터베이스에 저장됐음을 확인합니다.

 

요청 - 응답


지금까지 이벤트 컨슈머의 즉시 응답이 필요하지 않은 비동기 요청만을 살펴보았습니다. 하지만 각 프로세스 간에 동기적인 통신이 필요한 경우는 어떻게 대처할 수 있을까요?

이벤트 기반 아키텍처는 동기 통신이 필요한 경우 요청 - 응답 메시징 방식으로 수행할 수 있습니다. 요청 - 방식 메시징 내부의 각 이벤트 채널은 요청 큐, 응답 큐로 구성됩니다.

 

💡 첫번째 방법 : 메시지 헤더에 상관 ID 사용

 

과정 1

 

  • 이벤트 프로듀서는 요청큐에 메시지를 보내고, 고유한 메시지 ID를 기록합니다. 아직 상관 ID(CID)는 NULL 입니다.

과정 2

 

  • 이벤트 프로듀서는 메시지 필터로 응답 큐를 차단 대기합니다. 이때 응답 큐에는 상관 ID 124와 일치하는 것이 없으므로 아직까지 대기하게 됩니다.

과정 3

 

  • 이벤트 컨슈머 메시지(ID 124)를 받아 처리합니다.

과정 4

 

  • 이벤트 컨슈머는 응답 메시지를 생성하고 메시지 헤더의 상관 ID를 원메시지 ID로 설정합니다.

과정 5

 

  • 이벤트 컨슈머는 새 메시지를 응답 큐로 보냅니다.

과정 6

 

  • 2단계의 메시지 셀렉터와 상관 ID가 일치하므로 이벤트 프로듀서는 메시지를 수신합니다.

 

 

 

 

 

참고

 

 

 

 

 

728x90
반응형

'Architecture' 카테고리의 다른 글

Space Based Architecture  (0) 2023.09.21
Service Based Architecture  (0) 2023.09.13
Microkernel Architecture  (0) 2023.09.09
PipeLine Architecture  (0) 2023.09.07
Layered Architecture  (1) 2023.09.06