MSA
MSA(MicroService Architecture)는 소프트웨어 개발 기법의 하나이다.
MSA는 단일 애플리케이션을 작은 서비스 모음으로 개발하는 접근 방식으로, 각 서비스는 자체 프로세스에서 실행이 되고 느슨한 연결(Loosely-coupled) 구조로 만들어 HTTP 리소스인 REST와 같은 경량 메커니즘과 통신한다.
한마디로 "하나의 큰 애플리케이션을 여러 개의 작은 애플리케이션으로 쪼개어 변경과 조합이 가능하도록 만든 아키텍처" 이다.
Monolothic(모놀로식) 아키텍처는 ui - Business Logic - DataLayer 등으로 나누어져 1개의 서비스를 개발하는 것으로, 과거 많이 사용하던 방법이다. 최근 넷플릭스가 MSA로 전환을 성공하고, 넷플릭스 OSS라고 불리는 오픈소스 기반 프레임워크가 레퍼런스가 되며 많은 기업이 MSA로 전환을 시도 중이다.
하지만, 여태 MSA가 사용되지 못했던 데에는 아래와 같은 단점들이 있다.
1)개발 복잡도 & 숙련도
분산 시스템 개발은 복잡하다. 각각의 서비스를 독립적으로 관리해야하기에 각 모듈의 인터페이스를 신중하게 처리해야하고, 요청에 대한 방어 코드도 작성해야한다. 또한 주로 사용되었던 동기적 처리 방식인 Rest통신으로 각 서비스가 통신하게될 경우, 많고 복잡하게 얽힌 통신관계로 인해 구현이 어려워진다.
2) 트랜잭션 관리
MSA는 DataBase per Service, 즉 각각의 독립된 서비스 별로 별도의 DB를 가지도록 설계해야한다. 그렇기에 분리된 DB들 간의 트랜잭션 관리가 어렵다. 또한 데이터 동기화도 신경써야한다.
3)통합 테스트의 어려움
MSA기반 애플리케이션의 경우, 다수의 독립된 서비스를 포함하는 통합 테스트를 수행하기 전, 각 서비스의 의존관계를 파악해야한다.
4)배포 복잡도
MSA는 독립된 각 서비스를 개별적으로 배포해야한다.
MSA는 위와 같이, 상용화 되기에 몇가지 문제점들이 있었다. 하지만, 자동화 도구, 그리고 EDM(Event-Driven-Architecture)와 같은 아키텍처의 등장으로 MSA 기반의 애플리케이션을 구현하는 것에 용이해져 요즘 널리 알려지고 있는 개념이다.
EDM에 앞서, Event-Driven 개념에 대해 알아보자.
Event-Driven
Event-Driven은 예전부터 존재하는 개념이었지만, 최근 2018년 가트너에서 선정한 "유망한 기술" 에 선정되기도 하며 개발 트렌드 중 하나로 뽑히기도 했다.
Rest 방식의 통신과 다르게, 이벤트를 통해 느슨한 구조로 요청을 생성하기에, Consummer는 자신이 소비하는 서비스의 세부 정보를 알 필요가 없다.
Event Bus(Store)
이벤트는 다양한 방법으로 게시할 수 있다.
EDA에서는 보통 비동기 메세지 통신을 사용하는데, Publisher 와 Subscriber가 동기 통신 처럼 완전성을 보장하지 않기에,
메시지를 처리하는 중간 계층 서비스인 메세지 브로커, 혹은 이벤트 브로커를 두게 된다.
두 경우 모두 Producer(Publisher)는 이벤트를 발행하고 Consumer(SubScriber)는 해당 이벤트를 수신하여 그에 따른 처리를 수행한다. 메세지 브로커는 대표적으로 ActiveMQ, RabbitMQ가 있으며 메시지 모델로는 대표적으로 Apache Kafka를 많이 활용한다.
- 아파치 아프카: MSA 아키텍처 기반 이벤트 브로커. (이벤트 브로커는 메세지 브로커의 역할도 수행)
- 래빗엠큐, 액티브 엠큐: 모놀리식 아키텍처 기반 메세지 브로커
Kafka는 처리량이 많은 분산 메시징 시스템에 적합하고 RabbitMQ, ActiveMQ는 신뢰성 있는 메시지 브로커가 필요한 경우 적합하다.
이제 Producer와 Consumer 사이에서 중간다리 역할을 수행하는 메세지 브로커, 이벤트 브로커에 대해 자세히 알아보자.
메시지 브로커
메시지 브로커는 publisher가 생산한 메시지를 메시지 큐에 저장하고, 저장된 데이터를 consumer가 가져갈 수 있게 해주는 미들웨어 역할을 수행한다.
보통 서로 다른 시스템 사이에서 데이터를 비동기 형태로 처리하는 역할을 수행한다.
이러한 구조를 보통 pub/sub 구조라고 하며 대표적으로 RabbitMQ 소프트웨어가 있고 GCP의 pubsub, AWS의 SQS 같은 서비스가 있다.
이와 같은 메시지 브로커들은 consumer가 큐에서 데이터를 가져가게 되면 즉시 혹은 짧은 시간 내에 큐에서 데이터가 삭제되는 특징이 있다.
장점
- 메시지 큐를 사용하여 메시지를 안정적으로 저장하고 전달.
- 다양한 시스템 간의 비동기 통신을 돕는다.
- 다양한 메시지 패턴을 지원하여 유연한 메시지 처리가 가능
단점
- 메시지 큐에 대한 오버헤드가 발생할 수 있다.
- 메시지 전달의 지연이 발생할 수 있다.
- 처리량과 성능이 이벤트 브로커에 비해 상대적으로 낮다.
이벤트 브로커
이벤트 브로커는 기본적으로 메시지 브로커의 큐 기능들을 가지고 있어 메시지 브로커의 역할도 할 수 있다. 가장 중요한 차이는 이벤트 브로커는 메시지 브로커 역할을 할 수 있지만, 메시지 브로커는 이벤트 브로커 역할을 할 수 없다.
또한 이벤트 브로커는 publisher가 생산한 이벤트를 저장한다.
따라서 consumer가 특정 시점부터 이벤트를 다시 consume 할 수 있는 장점이 있다.
ex) 장애가 일어난 시점부터 그 이후의 이벤트들을 다시 처리
대용량 데이터 처리에 있어서 메시지 브로커보다는 더 많은 양의 데이터를 처리할 수 있는 능력이 있다.
장점
- 이벤트 주도 아키텍처를 적용할 수 있어 느슨한 결합과 확장성을 제공.
- 이벤트를 실시간으로 처리하고 복잡한 이벤트 흐름을 관리할 수 있다.
- 대규모 분산 시스템에서 처리량과 성능이 높으며, 고가용성과 내구성을 제공.
단점
- 어렵고 복잡하다
when?
각 브로커들은 어떨 때 유용하게 쓰일까?
- 이벤트 브로커
EDA: 이벤트 브로커는 이벤트 주도 아키텍처의 중심 요소로 사용되며, 이벤트를 발행하고 구독하는 시스템 간 통신을 관리한다. 이벤트 중심 시스템에서 이벤트 흐름을 조정하고, 느슨한 결합과 확장성을 제공하는 데 적합하다.
대규모 분산 시스템에서의 실시간 이벤트 처리(Reactive): 이벤트 브로커는 고성능 및 고가용성을 제공하여 대규모 분산 시스템에서 실시간 이벤트 처리에 적합하다. 대용량의 이벤트를 처리하고, 이벤트 흐름을 조정하며, 여러 구독자에게 이벤트를 전달할 수 있다.
- 메시지 브로커
안정적인 메시지 전달이 중요한 경우: 메시지 브로커는 메시지 큐를 사용하여 메시지를 안정적으로 저장하고 전달할 수 있습니다. 메시지 손실을 최소화하고, 메시지 전달의 신뢰성을 보장해야 하는 상황에서 메시지 브로커를 사용할 수 있습니다.
그렇기에, 요즘 대세인 MSA,Reactive의 키워드로 인해 각 서비가 분산되어 관리되며, Reactive한 시스템을 만들기 위해
EDA 아키텍처를 사용하고, 이벤트 브로커인 Apache Kafka(아파치 카프카)의 중요성이 강조되고 있다.
Reactive, MSA 시점에서 EDA에 대한 정보를 조금 더 알아보고 싶다면, 아래 글을 참고하자.
https://codenme.tistory.com/126
EDA에 대해 대략적으로 알아보았으니, 이제 MSA에서 각 서비스들이 서로 내부 통신을 수행하는 방법에 대해 알아보자.
비동기 메세지 큐를 통한 시스템 내 통합(integration)
MSA에서 내부 통신은 크게 2가지가 사용된다. 두가지 방식 모두 강점과 약점이 있기에 상황에 따라 적합한 방식을 선택하면 된다.
- REST 통신(Request-Response)
- 메세지 큐 통신
REST 통신
동기 통신이기에 응답 서비스가 항상 떠 있어야 하고 응답 서비스에서 오랜 시간을 소비하면 그만큼 응답이 늦어지기에 Blocking 모델이다( sync & Blocking )
또한 요청 체인이 길어지면 장애 전파(Fault Spread)의 위험이 있다.
REST 통신은 실시간으로 보여줘야 하는 데이터를 모으는 등 조회 기능을 수행하기 적합하다. 하지만 모든 내부 통신을 REST로 수행할 경우 몇가지 문제점이 있을 수 있다.
- 서비스 간 의존성
- 여러 서비스간 데이터 생성,삭제,변경이 얽혀있다면 어느 서비스에서 Client의 Request를 받아서 누구에게 전달해 처리해야 하는지 모호하다.
- 서비스가 추가된다면 해당 기능 수행에 연관있는 모든 어플리케이션에 수정이 필요할 수 있다.
- 시간이 지날수록 단일 기능의 서비스에서 서비스 오케스트레이션으로 변질될 가능성이 있다.
2. 데이터 관리 매우 어려움
- 서비스 간 분산 트랜잭션 처리 및 중간에 오류가 발생했을 때 복원 시점, 복원 키 값을 찾는 등에 어려움이 있다.
- 서비스 간 반정규화 된 동일한 데이터를 변경할 때 요청 받은 서비스가 반정규화된 데이터가 위치한 서비스를 모두 찾아서 변경하도록 처리해야 한다. 데이터 오너 서비스가 반정규화 해간 서비스 리스트를 관리하지 않는 이상 그에 따른 데이터의 일관성을 유지하기 어렵다.
3. 동기 통신의 비용
- 외부에서 API G/W를 통해 요청이 전달됐을 때, 응답을 하기 까지 연관된 모든 서비스의 자원을 홀딩한다.
메세지 큐 통신 ( Pub-Sub)
- 발행-구독 모델은 비동기 통신에 Broadcasting이 가능하다.
- Async & non-blocking: 비동기 통신이기에 수신자의 서비스가 항상 떠 있지 않아도 게시자는 메시지를 발송할 수 있다. 또한, 이벤트 Publisher는 SubScriber의 주소를 몰라도 되고 이벤트를 수신했는지 여부를 파악하지 않아도 되는 Non-Blocking 모델이다.
- 이로 인하여 장애가 격리(Fault Isolation) 된다.
비동기 메세지 큐 방식은 시스템 내 통합에 적합하다.
- 서비스 간 결합도가 낮아진다.
- 서비스의 흐름이 단순해진다.
- 발행 서비스는 구독 서비스들을 고려할 필요 없이 데이터의 생성,변경,삭제(CUD)가 발생하면 이벤트를 발행한다.
- 구독 서비스는 메세지 큐의 라우팅 룰에 따라 전달되는 이벤트를 구독해 자신만의 비즈니스 로직을 수행한다.
- 응답 지연시간이 낮아진다.
- 실시간으로 처리 및 전달해야하는 기능을 먼저 수행하고 나머지 후속동작은 메세지 큐를 통하게 적용한다면 응답 지연 시간을 낮출 수 있다.
이러한 pub-sub 통신 모델은 EDA에서 자주 쓰이며, EDM에서 각 서비스 간 비동기 통신을 통한 내부 통합을 돕는다.
이렇게 MSA의 2가지 내부 통신에 대해 살펴보았다.
요즘은 Pub-Sub 통신 모델이 주로 쓰이는데, 그 이유에 대해 알고 싶다면, 아래의 글을 참고하자.
https://codenme.tistory.com/125\
이제 이러한 Pub-Sub 통신 모델 기반을 기반으로 이벤트 처리를 하여 MSA를 구성하는, EDM 방식에 대해 살펴보자.
Event Driven MicroService 란?
Event Driven MicroService(EDM)는 MSA가 적용된 시스템에서 이벤트 발생시 해당 이벤트 로그를 보관하고 이를 기반으로 동작하며, 비동기 통신을 통해 시스템 내 통합(integration)을 수행하는 Architecture 이다.
EDM은 이벤트 기반과 비동기 통신을 통한 시스템 내 통합(integration)이라는 핵심 개념을 바탕으로 실제 구현은 자유롭게 구성 가능하다.
이벤트
IT 영역에서 이벤트는 다양한 정의를 갖지만, 이 곳에서 언급하는 이벤트는 상태의 변경. 즉, 데이터의 변경,생성,삭제(CUD)를 통해 발생하는 서비스의 의미있는 변화를 뜻합니다.이벤트 로그를 보관
현재의 데이터는 상태 변경의 누적이라는 생각에서 시작합니다. 이 때 상태 변경은 이벤트를 뜻하고 이를 누적하는 행위는 이벤트 로그를 보관하는 것입니다. EDM 에서 생성된 이벤트는 반드시 보관되어야 합니다. 보관된 이벤트는 데이터의 현재 상태를 구성하는 근간이 됩니다. 또한, 보관된 이벤트를 바탕으로 장애 발생 또는 특정 요구사항에 따라 지정된 시점으로 복원을 수행합니다. 이벤트 로그를 보관하는 장소를 이벤트 스토어라 칭합니다.비동기 통신
amqp, mqtt, jms 등 메세징 프로토콜을 통한 메세지 큐 방식이 자주 사용됩니다. 서비스에서 데이터의 생성,변경,삭제(CUD)를 통해 이벤트가 발생하면 발행 서비스는 메세지의 형태로 이벤트를 발행하고, 해당 이벤트에 관심이 있는 서비스에서 구독을 수행합니다. 메세지 큐를 사용함으로 requeue/dlq 등의 기능을 활용할 수 있습니다.시스템 내 통합(integration)
이상적으로 구현된 MSA는 서비스 간 데이터 참조를 위한 내부 통신이 필요없지만, 현실적으로 서비스 간 내부 통신이 전혀 없는 시스템을 구현하기란 불가능에 가깝습니다. 다양한 사유로 여러 서비스 간 통신을 통해 연동이 발생합니다.
이벤트를 데이터의 생성,변경,삭제로 정의했기 때문에 MSA의 데이터 관리와 밀접한 연관성을 갖는다.
데이터는 현재의 상태를 나타내고 이는 보관된 데이터 변경,생성,삭제 기록 즉, 이벤트 로그에 기반한다.
- 특정 서비스에서 기능을 수행
- 이벤트가 발생(데이터의 생성,변경,삭제)하면 해당 도메인 객체를 기반으로 이벤트를 생성
- 생성된 이벤트는 저장 공간에 보관되고 비동기 메세지 큐로 해당 이벤트에 관심이 있는 서비스들에게 전달
- 이벤트를 구독한 서비스는 비즈니스 로직을 수행
- 수행 도중 오류가 발생하면 저장된 이벤트 로그를 기반으로 retry/rollback을 수행
MSA는 나뉘어진 서비스와 서비스 별 각자의 데이터베이스 구성을 지향한다.(DataBase Per Service)
이로 인해 새로운 요구사항들이 생기는데, 이를 EDM을 적용해 충족시킬 수 있다.
서비스 별 각자 데이터베이스를 적용한 시스템에서 데이터의 무결성을 보장할 수 없지만 EDM을 통해 데이터의 최종적인 일관성을 유지할 수 있다.
- all commit or rollback → eventually consistency
이벤트 기반으로 마이크로서비스를 확장하려는 의도는 여러 공개된 정보에서 읽을 수 있다. Spring One 의 메인 주제 중 하나로 Event Driven 이 선정되고 있다. 여러 기업들이 컨퍼런스 및 기술 블로그를 통해 Event Driven 도입을 알리는 것을 확인할 수 있다.
우아한 형제들 — 박용권 님, Spring Camp 2018 세션 내용 참고
왜 Event Driven MicroService을 적용하는가 ?
MicroService Architecture가 Event Driven 과 연결되어 언급되는 이유는 무엇일까?
MicroService에 Event Driven 을 같이 사용하자!
MSA를 도입하여 느슨한 결합, 관심사의 집중, 단일 책임 원칙, 빠른 배포주기, 폴리글랏, Scalability, 장애의 격리, 유연성, 확장성 등 여러 이점을 확보할 수 있다. 하지만 이를 위해 넘어야 하는 허들이 있다.
MSA를 적용한 시스템은 서비스, Database가 각각 분리되어진다. MSA 에서 주요 원칙 중 하나는 서비스 별 자체 비즈니스 로직과 데이터, 그에 따른 최적의 Database를 선택하는 것입니다. 이상적으로 하나의 기능을 수행할 때 자신의 서비스 내에서 모두 해결할 수 있도록 서비스 분리를 잘한다면 시스템 내 통합(integration)을 고민하지 않아도 된다. 하지만 현실적으로 서비스 분리에 따른 서비스 간 데이터 참조 등 시스템 내부에 연결이 발생합니다. 또한, 서비스가 뭉쳐져 있을 때 발휘되던 장점을 활용하지 못하는 경우도 발생한다.
Database Per Servic
MSA에서 중앙화된 Shared Relation DB를 사용하는 것은 많은 제약사항을 가지고 있다.
그렇기에 Database Per Service 는 MSA의 느슨한 결합, 관심사의 집중, 폴리글랏 프로그래밍, 독립적인 배포 주기 등을 달성하기 위한 핵심 키워드입니다. 하지만 MSA를 적용할 때 Database Per Service는 가장 어려운 부분 중 하나이다.
그리고 이런 기술적 어려움을 해결하는데 Event Driven 이 효율적으로 동작한다.
Database를 변경/분리시 고려해야 하는 요소들이 있다.
1. 아래와 같은 Shared 관계형 데이터베이스 장점 사용 불가
- 데이터를 효율적으로 보관하고 조회/삭제 등 기능의 효율을 높이는 장점
- 테이블 조인을 통한 통합 뷰 제공
- ACID 원칙에 따른 트랜잭션 기능
2. database oriented system
- 모든 업무의 정의는 Database 스키마/테이블 설계부터 시작
- 결국 시스템의 성격은 데이터의 CRUD기능, 데이터의 흐름과 life cycle이 중요
- → 디비 분리는 결국 기존 데이터의 흐름을 깨는 행위
3. 데이터베이스 분리시 발생하는 비용
4. 기존 DBMS에 최적화된 각종 세팅
위와 같은 이유로 Database Per Service를 적용하는 것은 어렵다. 하지만 중앙화된 Shared Database를 사용하는 것은 많은 제약사항이 있다.
- 중앙화된 Shared Database 데이터베이스는 시스템의 응집력을 저해하고 종속성을 높인다.
- 단일 트랜잭션 처리에 따라 테이블 락 등 장애 발생 가능성이 있다.
- 중앙화 된 데이터베이스에 장애 발생시 전체 시스템에 장애를 일으킨다.
- 데이터베이스 스케일링이 어렵다.
- 서비스의 특징에 따른 최적의 데이터베이스 선택이 어렵다.
기존의 Shared Database 구성에서 MSA의 이점을 살리기 어려운 이유는 명확하다.
하지만, 기존의 Shared Database 구성, 그 중 관계형 Database를 사용 하는 경우 ACID 원칙에 따른 트랜잭션 처리, 데이터 무결성, 모든 테이블에 접근해 Join을 통한 통합 뷰 제공, 관계를 활용한 효율적인 데이터 저장/조회 등 DBMS 레벨에서 여러 유용한 기능을 제공했습니다. Database Per Service를 적용하면 Shared Database 구성의 DBMS 레벨에서 제공하던 기능을 Application 레벨에서 해결해야 한다.
- 스텝에 따라 처리되는 비즈니스 흐름 수행
- 분산된 서비스 간 트랜잭션 처리
- 서비스 간 반정규화 데이터 동기 처리
Database Per Service 적용의 기술적으로 어려운 부분을 해결하는데 Event Driven 이 효율적으로 동작한다.
폴리글랏
폴리글랏: 단일 언어로 제공되지 않는 추가 기능과 효율성을 극대화 하기 위해 여러 언어로 코드를 작성하는 것
MSA의 Database Per Service를 적용해 각 서비스의 목적에 맞는 최적의 Database 를 선택할 수 있다.
모바일, SNS, IOT 등 다양한 시스템이 활발하게 발전하고 있습니다. 이들 시스템이 다루는 데이터는 스트림 형태의 비정형 데이터가 많습니다. 매우 빠른 읽기/쓰기 성능을 지원해야 하고 분산형 구조를 통해 데이터를 여러 대의 서버에 저장합니다. 상호 복제해 특정 서버에 장애가 발생했을 때에도 데이터 유실이나 서비스 중지가 없는 형태의 구조 등 기존 관계형 Database에 최적화 되지 않은 기능을 요구합니다. 확장성, 단순한 구조, 느슨한 정합성, 낮은 비용, 빠른 검색 등의 NoSQL Database 가 적합합니다.
기존의 시스템에서 서로 다른 Database를 사용하는 경우 이기종 Database 간 트랜잭션 처리가 어려웠다. 하지만 MSA에서는 다른 서비스의 데이터를 참조할 때, 직접 접근하지 않고 데이터의 캡슐화를 통해 데이터 오너 서비스의 API를 통해서만 접근할 수 있게 한다. EDM은 DBMS 레벨이 아닌 Application 레벨에서 트랜잭션 처리를 수행하는데 적합해 서비스의 목적에 최적화된 Database를 선택하는데 도움을 준다.
트랜잭션 처리
설명을을 위해 간단하게 핫초코를 구매하는 과정을 생각해보자
- 메뉴 선택
- 핫초코 주문 ← 상태 변화
- 계산 ← 상태 변화
- 핫초코 만들기 ← 상태 변화
- 핫초코 먹기 ← 상태 변화
핫초코 구매라는 하나의 기능이 여러 단계에 걸쳐서 수행된다. 각 단계마다 상태 변화를 동반합니다. 이전 단계를 완료하면 그에 반응해서 다음 단계를 수행한다.
MSA에서 각 단계별로 서비스를 구현했다면 menu → order → pay → make → delivery 서비스 순서대로 동작한다.
이 연결 관계에서 비즈니스 흐름(데이터 흐름)을 파악하는 것은 기능 수행을 위해 매우 중요하다.
이 일련의 과정에 EDM 을 적용해 상태가 변경되면 이벤트를 발생,발행하고 이를 관심있는 서비스가 수신 후 기능을 수행하면서 비즈니스 흐름에 따라 각 서비스의 기능을 수행할 수 있다.
이번엔 오류가 발생하는 상황을 가정해보자.
- 메뉴 선택
- 핫초코 주문 ← 상태 변화
- 계산 ← 상태 변화
- 핫초코 만들기: 핫초코 재고 부족 ← 오류
- 환불, 주문 폐기 ← rollback
기존의 Lagacy 시스템에서 문제 발생시 일관된 commit 또는 rollback 처리나 이전에 발생한 상태 변경에 직접 접근해서 데이터 수정이 가능합니다. 하지만 MSA가 적용된 시스템에서 서로 다른 서비스에 걸쳐진 기능을 수행하는 도중 일관된 commit 또는 rollback을 수행할 수 없다 (DPS를 통해 DB가 분리되어있다!).
이 때 EDM을 적용해 rollback 또는 retry를 처리할 수 있습니다. rollback이 필요한 경우 Failed 이벤트를 발생시키고, 이를 이전 스텝을 수행한 서비스에서 구독하여 보관되어 있던 이벤트 로그 기반으로 rollback을 수행한다. retry가 필요한 경우 메세지 큐의 requeue 또는 dead letter queue 기능을 사용해 retry 처리를 수행할 수 있다.
반정규화 데이터의 동기 처리
MSA가 적용된 시스템에서는 비즈니스 로직과 이를 수행하는데 필요한 데이터가 서로 다른 서비스에 나뉘어진 경우가 있을 수 있다. 처음에는 여러 서비스 간 REST 통신을 통해 데이터 참조를 수행할 수 있으나, 서비스가 커져가면서 복잡성/성능 등의 사유로 서비스 간 데이터 반정규화가 필요할 수 있다.
정규화란?
- 정규화는 데이터의 일관성, 최소한의 데이터 중복, 최소한의 데이터 유연성을 위한 방법이며 데이터를 분해하는 과정이다.
- 정규화된 모델은 테이블이 분해된다. 테이블이 분해되면 직원 테이블과 부서 테이블 간에 부서코드로 조인(join)을 수행하며 하나의 합집합으로 만들 수 있다.
- 정규화를 하면 불필요한 데이터를 입력하지 않아도 되기 때문에 중복 데이터가 제거된다.
정규화 절차
정규화의 문제점
- 정규화는 데이터 조회(select) 시에 조인(join)을 유발하기 때문에 CPU와 메모리를 많이 사용한다.
데이터 반정규화(Data Denormalization)란?
관계형 데이터베이스에서 정규화된 데이터 모델을 포기하고, 성능 향상이나 데이터 조회를 간소화하기 위해 중복 데이터를 포함한 비정규화된 데이터 모델을 사용하는 것을 의미한다.
- 조인으로 인하여 성능이 저하되는 문제를 반정규화로 해결할 수 있다.
- 반정규화는 데이터를 중복시키기 때문에 또 다른 문제점을 발생시킨다.
일반적으로 정규화는 데이터를 여러 테이블에 분할하여 중복을 최소화하고 데이터의 일관성과 정확성을 유지하는데 목적이 있다.
MSA 환경에서 여러 서비스 간의 데이터 공유와 통신을 수행할 때, REST 등의 방식으로 데이터를 참조하면 복잡성과 성능 문제가 발생한다. 따라서 데이터 반정규화를 통해 중복 데이터를 허용하고 각 서비스 내에 필요한 데이터를 중복으로 저장함으로써 데이터 조회 속도를 향상시키고 복잡성을 감소시킨다. 이를 통해 서비스 간 데이터 동기 처리를 보다 효율적으로 수행한다.
이처럼 서비스간 데이터 반정규화가 발생할 때, 해당 반정규화 동작에 대한 이벤트를 통해 서비스 간 데이터 동기 처리를 수행할 수 있다.
간단한 예시를 통해, EDM 이 어떻게 데이터 반정규화 상황 발생 시 데이터 동기화를 수행하는지 알아보자.
반정규화 상황 예시
상황
- 마이크로서비스 product, category로 구성된 백엔드
- product는 category의 api를 호출해서 기능 수행
- 비즈니스 로직이 복잡해진다던지, 성능 문제가 생긴다던지 등의 반정규화 needs가 발생
- product의 Database에 category의 데이터 categoryName 반정규화
반정규화된 데이터를 동기화하는 과정
- category 의 name 변경시 이벤트 발생,발행
- product 에서 CategoryEdited 이벤트를 구독해 categoryName 변경사항 반영
비동기 메세지 큐를 통한 시스템 내 통합(integration)
앞서 말했듯이, MSA에서 내부 통신은 크게 2가지가 사용된다.
- REST 통신
- 메세지 큐 통신
또한 비동기 메세지 큐 방식은 시스템 내 통합에 적합하다는 것도 확인했다.
Event-Driven 통신은 비동기 메세지큐를 통해 통신하기에, EDM이시스템 통합에 적합한 아키텍처가되는 것이다.
Eventually Consistency
기존의 DBMS 활용한 ACID 트랜잭션에 따른 데이터 무결성 보장은 MSA 에서 서비스, Database가 나눠짐으로써 더이상 달성할 수 없게된다.
대신 EDM을 사용해 데이터의 최종적인 일관성 유지로 변경된다.
- all commit or rollback → eventually consistency
eventually consistency 란?
Eventually Consistency는 분산 시스템에서 데이터 일관성을 보장하기 위한 일종의 일관성 모델이다.
MSA(Microservices Architecture)와 같은 분산 시스템에서는 여러 개의 서비스와 데이터베이스가 분리되어 동작하므로, 기존의 ACID 트랜잭션 모델을 통한 데이터 무결성 보장이 어렵다. 이러한 환경에서 Eventually Consistency를 적용하여 데이터 일관성을 유지할 수 있다.
Eventually Consistency는 데이터의 변경이 시간에 따라 일정 기간 내에 모든 복제된 노드에 반영되어 최종적으로 일관성을 유지하는 것을 의미한다. 이 모델에서는 모든 업데이트 작업이 즉시 모든 노드에 반영되는 Strong Consistency와 달리, 일정 시간이 지난 후에 복제된 노드에 변경 내용이 도달하게 된다.
이러한 동작 방식은 네트워크 지연, 분산 환경의 복잡성, 장애 등의 요인으로 인해 복제된 노드들 사이의 데이터 동기화에 지연이 발생할 수 있음을 의미한다.
하지만 시스템이 충분한 시간과 리소스를 가지고 데이터 동기화를 수행하면, 일정 시간이 지난 후에는 모든 복제된 노드가 최종적으로 동일한 상태를 유지하게 된다.
Eventually Consistency는 분산 시스템에서의 가용성과 확장성을 보장하면서 일관성을 유지할 수 있는 중간 지점을 제공합니다. 개별 서비스 간의 트랜잭션 처리가 아닌, 데이터의 변경을 비동기적으로 처리하여 일관성을 유지하고, 필요에 따라 충분한 시간과 리소스를 투자하여 모든 복제된 노드를 동일한 상태로 만들어 간다.
Event Driven MicroService의 요구사항 및 달성 효과는 다른 개념들에서도 찾아볼 수 있다.
DBMS 내 트랜잭션 로그를 활용한 redo/undo 로직, 이기종 Database 간 분산 트랜잭션 처리, 외부 시스템과 I/F를 통해 업무를 처리할 때 상이한 시스템 간 정합성 처리 등 기존에 다루던 문제들과 요구사항 및 구현 방식이 비슷하다.
따라서 비슷한 문제를 해결하는 방식들을 참고해 필요한 Insight를 도출할 수 있다.
요약
MicroService에 Event Driven을 엮으면 MSA를 도입하면서 새로 발생한 요구사항(허들)을 달성할 수 있다.
- 비즈니스 흐름에 따른 로직 수행
- 분산 트랜잭션 처리
- 서비스 간 반정규화 데이터 동기 처리
- 적절한 시스템 내 통합(EDA<->메시지큐 통신)
- 최종적인 일관성
1) Database Per Service
MSA에서 중요한 Database per Service개념에 Event-Driven 방식이 적합하다.
Database Per Service는 MSA의 핵심 개념 중 하나로, 느슨한 결합, 관심사의 집중, 폴리글랏 프로그래밍, 독립적인 배포 주기 등을 가능하게 한다.
Database Per Service를 적용하면 각 서비스마다 별도의 DB를 가지게 되기에, 기존의 RDB 구성에서 DBMS 레벨에서 제공하던 다양한 편리한 기능들을 애플리케이션 레벨에서 해결해야한다.
이를 가능하게 하는 것이 Evetn-Driven 방식이다.
EDM을 통해 Database Per Service를 적용하면 애플리케이션 레벨에서 트랜잭션 처리를 수행할 수 있으며, 서비스의 목적에 맞는 DB를 선택하는 데 도움을 준다.
2) 트랜잭션 처리
비즈니스 흐름에 따라 기능을 수행하다가 중간에 문제가 발생할 수 있다. 문제가 발생한 시점에 롤백(Rollback) 또는 재시도(Retry)를 수행해야하는 시점이 있다.
MSA가 적용된 시스템에서는 서로 다른 서비스에 걸쳐진 기능을 수행하는 도중 일관된 commit 또는 롤백을 수행할 수 없다. ( 서비스마다 DB 분리되어있는데, 서로 분리된 DB에 어떻게 트랜잭션 처리를 할 것인가?)
이때 EDM을 적용해 롤백 또는 재시도하여 처리할 수 있다. 롤백이 필요한 경우 Failed 이벤트를 발생시키고 이를 이전 스텝을 수행한 서비스에서 구독하여 보관되어 있던 이벤트 로그 기반으로 롤백을 수행한다. 재시도가 필요한 경우 메시지 큐의 Requeue 또는 Dead-Letter Queue 기능을 사용해 재시도 처리를 수행할 수 있다.
3) 반정규화 데이터의 동기 처리
MSA 환경에서 여러 서비스 간의 데이터 공유와 통신을 수행할 때, 성능 등의 요인으로 데이터를 반정규화해야될 경우가 있다.
EDM은 데이터를 반정규화된 형태로 이벤트를 통해 데이터를 전파하고 동기화함으로써 서비스 간의 데이터 일관성을 유지한다.
4) 최종 일관성(Eventually Consistency)
기존의 DBMS를 활용한 ACID 트랜잭션에 따른 데이터 무결성 보장은 MSA에서 서비스, DB가 나누어 가짐으로써 더는 달성할 수 없다. 대신 EDM을 사용해 데이터의 최종적인 일관성을 유지할 수 있다.
( all commit or rollback → eventually consistency )
데이터 업데이트 이벤트 발생 시, 업데이트를 바로 반영하지 않고, 일정 시간 이후 복제된 노드에 반영하고, 시스템이 충분한 시간과 리소스를 가지고 동기화함으로써 최종 일관성(Eventually Consistency)를 얻어 서로 다른 서비스, DB에 있는 데이터들의 통합적인 일관성을 유지한다.
정리
EDM은 MSA 도입 시 발생하는 새로운 요구 사항을 달성하는 개념이다. ( MSA 도입시, 서비스간 Event-Driven방식으로 통신하는 것)
- 서비스, DB의 분리, 폴리글랏 등에 대응해 효과적으로 동작한다.
- 실패를 허용하는 분산 구조, 이벤트 브로커 등을 통해 문제를 해결하고 MSA 도입의 난관을 해결할 수 있도록 도와준다.
기존의 모놀로틱 아키텍처의 단점 및 한계를 해결하고자 하거나 클라우드 환경에서 시스템을 구축할 때 얻을 수 있는 장점 때문에 MSA 도입을 결정했다면 그 다음 닥칠 문제에 대비해 EDM을 고려해볼 필요가 있다.
EDM 아키텍처 예시
서비스 간 통신 방식
- 조회 기능은 REST 통신을 수행
- 데이터 변경 이벤트 및 보상 트랜잭션 처리를 위한 이벤트는 메세지 큐를 통해 동작
조회 기능은 주로 실시간으로 데이터를 반환해야 하는 경우가 많다. 메시지 큐를 통해 데이터 조회를 처리하면 추가적인 지연이 발생할 수 있으며, 실시간성이 저하될 수 있다.
따라서, 이벤트 기반 아키텍처에서는 조회 기능과 데이터 변경 기능을 분리하여 설계하고, 데이터 변경 이벤트를 처리하기 위해 메시지 큐를 활용하는 것이 일반적이다.
event loop
- 서비스의 상태 변경 → 이벤트 발생 → 이벤트 버스(브로커)를 통해 전파 → 관심있는 서비스에서 구독 → 반복 …
- 서비스 로직 수행 중 오류 발생 → fail 이벤트 발생 → 이벤트 버스(브로커)를 통해 전파 → 관심있는 서비스에서 구독 → 저장된 이벤트 로그를 조회해서 한 덩어리로 묶여야할 이벤트를 찾고 rollback ← 반복 …
Conclusion
EDM은 MSA 도입시 발생되는 새로운 요구사항(허들)을 달성하는 개념이다. 서비스, Database의 분리, 폴리글랏 등에 대응해 효과적으로 동작하기에 MSA 도입시의 다양한 문제를 해결하고 이점을 강화할 수 있다.
EDM에서 이벤트는 데이터의 생성,변경,삭제로 정의한다. 이벤트가 발생하면 이벤트 스토어에 보관하고 메세지 큐를 통해 발행한다. 해당 이벤트에 관심이 있는 서비스에서 구독 후 정해진 비즈니스 로직 을 수행한다. 비즈니스 로직을 수행한 결과 이벤트가 발생하면 이 또한 이벤트 스토어에 로그의 형태로 보관하고 메세지 큐를 통해 발행한다.
보관된 이벤트 기록은 장애 또는 특정 요구사항에 따라 데이터를 복원하는데 사용된다. 이 때 복원 시점, 복원 키값을 등으로 서로 분리된 서비스간에 한 덩어리로 묶여야할 이벤트들을 rollback, retry하는 등 작업을 통해 eventually consistency를 얻을 수 있다.
EDM의 Architecture는 RDB + RabbitMQ, Kafka, Kafka + KSQL, Reactive + NoSQL, Serverless 등 Application과 Backing Service의 조합으로 굉장히 다양하게 구성할 수 있다. 유용한 Backing Service를 통해 핵심 factor 를 우선 달성하고 미비한 기능은 Application으로 커버하면 변경사항을 줄일 수 있다.
EDM은 이벤트 기반과 비동기 통신을 통한 시스템 내 통합(integration)이라는 핵심 개념을 바탕으로 실제 구현은 자유롭게 구성 가능하다. 특정 방식을 따르기보다 조직/개인의 역량에 알맞는 방법을 선택하는 것이 가장 중요하다.
시스템을 Cloud 환경에 구축할 때 얻을 수 있는 이점과 MicroService Architecture의 장점에 끌려서 Monolitic Architecture가 아닌, MicroService Architecture의 도입을 결정했다면 EDM 아키텍처의 적용을 고려해보자.
'MSA, EDA, Reactive 패러다임' 카테고리의 다른 글
EDM 아키텍처를 통한 통합 거래 및 이상 거래 탐지 시스템 구축(PT 발표 자료 및 스크립트) (0) | 2023.09.11 |
---|---|
Node.js기반 Express VS java기반 Spring WebFlux (0) | 2023.07.08 |
Reactive System과 event-driven Architecture (0) | 2023.07.06 |
Request-Respone(Rest 통신) VS 비동기 메세지 통신(Pub-Sub) (0) | 2023.07.06 |
reactive programing [이론 정리] (0) | 2023.07.06 |