왜 MSA 를 사용해야할까?
다양한 이유가 있을테지만, 그 이유는 차근차근 공부해나가면서 알아보기로하고 이론적으로 바로 느껴지는 이유들을 나열해보자.
- 모노리스한 서버라면 특정 부분을 조금만 수정해도 다른부분에 영향이 가는것이 있는지 알아봐야한다.
- 특정부분에서만 리소스가 필요한데, 어쩔 수 없이 다른 기능들도 스케일 업, 스케일 아웃을 해야한다.
- 특정 서비스만 패치가되어 배포를 해야하는데 다른 모든 서비스들도 배포가 되어야한다.
위에서 말한 내용은 비용적인 면에서도 개발적인 면에서도 둘다 효율적이지 못합니다.
MSA의 정의
어플리케이션이 여러개의 서비스조각으로 구성된 서비스를 의미합니다.
여기서 말하는 서비스는 각각 독립적인 서비스를 제공합니다.
서비스가 사용하는 저장소는 다른 서비스와 완벽히 격리됩니다.
Martin Folwer
- 여러개의 작은 서비스 집합으로 개발하는 접근방법
- 각 서비스는 개별 프로세스에서 실행
- Http 자원 API 같은 가벼운 수단을 사용하여 통신
- 각 서비스는 서로다른 언어, 데이터, 저장 기술을 사용
마이크로 서비스 성공을 위한 조건
- 인프라 : 언제나 쉽게 확장 가능한 유연한 인프라 구성
- 자동화 : 개발지원 도구 자동화, DevOps Infra (CI / CD)
- 개발 프로세스 : 피드백 기반의 개발 프로세스, 반복/점진적인 애자일 프로세스
- 개발 문화 : 공유/협업하고 학습하여 계속 진화하는 개발문화
- 설계 방식 : 데이터 중복, 결과적 일관성 추구 (데이터 중복을 활용해야함, 트랜잭션 중심에서 벗어나는것을 추구한다.)
개발 프로세스
기존에 개발 방식은 장기간동안 세세하게 개발일정을 세워놓고, 계획에 맞춰 개발을 했다면
이제는 빈번한 배포와 빠른 대처를 위한 프로젝트기반의 점진적인 개발을 해야합니다.
즉, 단기간안에 조금씩 변화 또는 패치를 하여 고객에게 빠른 대처와 시장의 간을 보는 기능을 할 수 있습니다.
설계방식
옛날보다 데이터 저장소의 가격이 감소하여 데이터 베이스 정규화의 필요성을 빡빡하게 가져갈 필요가 없습니다.
따라서 서버를 나누면서 그에따라 데이터베이스도 나누는데, 이때 같은 데이터를 다른 데이터베이스에서 담고있다고해서 그것을 억지로 없애거나 하는것을 지향해야합니다.
하지만 이런 설계방식은 데이터 일관성의 문제가 발생합니다. 여기서는 "지금시점에 데이터의 일관성이 틀릴지라도 어느시점에는 데이터의 일관성이 맞을거야" 라는 마인드로 접근해야합니다. (결과적 일관성)
특정 서버가 죽어도 다른서버가 살아있기에, 죽은것을 모르는 상황이 발생할 수 있습니다. 이는 주기적인 모니터링이 필요합니다.
No Sliver Bullet
만병통치약은 없다는 말입니다. 즉, MSA 가 모든것을 해결해주지는 않는다는 의미입니다.
오해들
- 컨테이너, 쿠버네티스를 사용하면 MSA를 하는것이 생각하거나
- 클라우드 인프라 위에는 무조건 마이크로 서비스가 올라가야한다거나
- 마이크로 서비스로 구축하면 시스템이 유연해지고, 확장성이 높아진다 생각하거나
하는 오해들이 있습니다. 하지만 모든 기술에는 Trade-Off 가 있고, 따라서 아키텍쳐에는 정답이 없습니다.
아래처럼 유명한 클라우드 전환 가이드를 보게되면
모노리스로 갈수록 성능, 내고장성, 비용, 안정 및 신뢰성이 증가되고, 마이크로 서비스로 갈수록 비즈니스 민첩성, 배포성, 탄력성, 진화성등이 우수해집니다.
인스턴스가 아무래도 쪼개져있기때문에 분산트랜잭션이 필요하게됩니다. 따라서 어쩔 수 없이 성능에 관련해서 불리하게 가져갈 수 밖에 없습니다. (CSP 의 문제가 발생할 우려)
쿠버네티스에 마이크로서비스만 올릴 수 있는것이 아니라 모노리스한 서비스도 올릴 수 있습니다. 이것만으로 더 높은 수준의 확장성, 배포성, 탄력성이 가져가지는 것입니다. (마이크로 서비스를 한다고해서 가져가지는것이 아니라는 의미)
또한 기존의 모노리스한 레거시를 마이크로 서비스로 변경시킬 때는 위와같이 왼쪽에서부터 오른쪽으로 천천히 가는것도 중요하다.
개인적인 생각
기존에 서비스단에서 Transaction 을 통해 관리되던 데이터의 일관성이 마이크로 서비스화 되면서 골치아파집니다.
이는 사가패턴을 기반으로 카프카가 분산트랜잭션을 관리하며 데이터의 일관성을 지켜야합니다.
또한 기존에 데이터베이스에서 하던 여러테이블에서 필요한 정보만을 가져오기위한 조인을 할 수 없게됩니다.
이는 단순히 보기에도 네트워크나 데이터베이스 조회면에서 불리하게 작용할 수 밖에 없다고 생각했습니다.
하지만 다른기준으로 MSA를 접근해보았습니다. MSA를 하는 기업들은 일반적으로 트래픽이 많은 기업일것입니다.
이는 Scale-up 을 아무리 많이 한 데이터베이스라도 하나의 인스턴스로는 버티기 힘들어보입니다.
그랬을 때, 데이터베이스에 접근하여 조회가 될때까지 기다리는 시간이 인프라관점에서는 더욱 리스크가 있는것으로 보입니다.
강의에서 설명을 들었듯 일반적인 상황에서는 네트워크 지연율은 0이라고 봐도 무방합니다. 따라서 너무 성능적인 면에서 불합리하다고 생각하지 않아도 될것으로 보입니다.
또한 데이터베이스는 트래픽이 많이 몰리는 도메인은 스케일 업으로 한계에 부딪혔을 때 리플리케이션을 만들어 관리하는것도 좋아보이고, 읽기전용 데이터베이스는 스케일 아웃, 쓰기전용 데이터베이스는 스케일 업을 하며 따로만들어 데이터의 일관성도 보전하면서 앞단에서의 로드밸런싱으로 효율을 최대한으로 올리는것도 좋아보입니다.
아키텍처란?
시스템이 가져야할 특성들을 반드시 필요한 특성들을 구분하고, 그 특성들을 지키게 하는것
아키텍처 싱크홀 안티패턴
일반적으로 레이어드 아키텍처로 구현을 하다보면 비지니스 단에서 아무런 로직이 없던 경우가 있을겁니다.
그럼에도 프리젠테이션 레이어에서 데이터베이스레이어로 바로 호출하는것이 아닌 비지니스를 거쳐 호출을 하곤했습니다.
이러한 상황에 한에서 레이어드 아키텍처가 필요하지 않다고 느끼게되는데 이것을 아키텍처 싱크홀 안티패턴이라 부릅니다.
** 레이어드 아키텍처는 MVC 가 아니다. 아키텍처적인 면에서 봤을땐
View 는 사용자에게 보여주는것
Controller 는 프리젠테이션 레이어
Model 은 View 에게 던져주는 것
따라서 MVC 패턴만으로는 레이어드 아키텍처를 설명할 수 없고, 백엔드 단에서 본다면 Presentation Layer를 정의하는 패턴입니다.
마이크로 커널 아키텍처
솔루션 제품군에 플러그인을 사용하는 아키텍처
이벤트 기반 아키텍처
확장성이 뛰어난 비동기 분산 아키텍처입니다.
각 서비스가 자율적으로 이벤트를 처리하는 방식을 코레오그래피라 부르고,
하나의 통제모듈을 통해서 하는방식을 오케스트레이터라고 부릅니다.
코레오그래피는 데이터 비일관성이나 에러처리의 어려움이 존재합니다.
오케스트레이션은 위의 단점을 해소해주지만 중재자가 병목지점이 될 수 있습니다.
SOA (Service Oriented Architechture)
MSA 와 유사한 서비스중심의 재사용에 초점을 맞추고있는 아키텍처입니다.
하지만 MSA 보단 더 큰단위로 아키텍처의 형태가 나누어져 있습니다.
SOA 는 모듈의 의존성은 줄이되 모듈 내에서 공유할 수 있는건 최대한 공유하는 정책을 사용합니다.
MSA 는 가능한 공유하지 않고, 모듈들이 독립적으로 운용될 수 있도록 아키텍처를 디자인합니다.
여기서 구별은 서비스의 Flow 또한 구별을 지향한다는것입니다. 예시로 서비스 내에서 결제를 하고자할 때, SOA 는 관련된 루틴을 수행하여 결제를 지원함으로써, 유저에세 제공해주는 "서비스"를 1차 목적으로 합니다.
반면 MSA 는 유저에게 관련된 루틴과 결제 루틴을 별도로 이용하게끔 합니다. 즉, 서비스 내의 독립이 아닌 독립된 서비스를 지향하는 것입니다.
그렇다보니 MSA 는 별도의 체계가 없는 경량화된 프로토콜을 운용하게됩니다.
MSA에서는 서비스 오케스트레이션이나 API GateWay 를 무조건 사용해야한다는 강박관념도 없애야합니다. -> 이 의견을 좀더 생각해볼 만한 여지가 있음
Scale up
기존 서버를 보다 높은 사양으로 업그레이드 하는 것입니다.
서버에 디스크를 추가하거나 CPU나 메모리를 업그레이드 하는것을 말합니다.
ex) AWS EC2 인스턴스 사양을 micro -> small -> medium 등으로 높이는 것입니다.
Scale out보다 관리비용이나 운영이슈가 적습니다.
성능 향상에 한계가 있으며 특정 성능부터 비용대비 성능향상이 적어집니다. 또한 서버 한대가 부담하는 양이 많아 천재지변등 다양한 이유로 서버에 문제가 생기면 큰 타격을 입게됩니다.
기존 서버를 교체함으로써 성능을 올릴때에는 서비스를 이용할 수 없는 다운타임이 불가피합니다.
하지만 데이터 정합성면에서 유리하기에, 데이터베이스 서버에 많이 사용되는 방식입니다.
Scale out
비슷한 사양의 서버를 추가로 연결해 처리할 수 있는 데이터 용량이 증가할 뿐만 아니라 기존 서버의 부하를 분담합니다.
확장의 유연성이란 장점이 있습니다. (무한한 확장)
서버의 용량을 어느정도 확보해 놓아야되는지 정확히 모르므로 Scale up과 같은경우에는 손해 볼 가능성이 높습니다.
Scale out 같은 경우에는 필요할 때마다 추가하면되니 손해 볼 가능성이 적습니다.
또한 서버 한대가 죽어도 다른서버가 살아있어 대응이 가능하고 이것은 즉, 한대의 서버에 병목현상이 발생해도 서버를 운영하는데 영향이 적다는 의미입니다.
하지만 서버의 수가 늘어남으로 데이터 정합성면에서 관리가 힘들어지는 부분이 있습니다.
콘웨이의 법칙
시스템을 설계하는 조직은 조직의 의사소통 구조를 본딴 설계를 구현하도록 되어있다는 법칙입니다.
이는 조직이 변화에 대비하고 있어야만 시스템의 설계 또한 설계가 자유롭게 변경될 수 있다는것을 의미합니다.
따라서 조직의 구조가 마이크로 서비스 아키텍처처럼 고스란히 반영되어야 개발팀과 서비스를 느슨하게 결합시킬 수 있습니다.
Bounded Context
야놀자 서비스에서 회원을 도메인 별로 의미를 지어보겠습니다.
결제에서 주문자
여행에서 여행자
숙박에서 숙박자
항공에서 탑승자
배송에서 수령자
이와 같이 하나의 회원이라는 도메인에서 여러가지 의미가 나옵니다. 따라서 이 회원이라는 도메인하나로 실제 여러가지 컨텍스트를 정의할 수 없으며 이러한 경계를 갖는 구분을 만드는것을 Bounded Context라고 불립니다.
물론 Bounded Context 에따라서 분리되어있는것도 이것만의 장점을 가져가긴하지만 이러한 Bounded Context 는 꼭 배포단위만큼 쪼개어질 필요가 없습니다. 동일 저장소에서 리팩토링을 통해 충분히 경계를 만들 수 있으므로 추후 서비스가 분리될때를 대비해 경계를 잘 구분지어 구현하는것이 중요합니다.
디자인 패턴
Two Phase Commit
분산 DB 환경에서 쓰는 방법으로 말 그대로 2단계에 거쳐 데이터를 영속화하는 작업입니다. 이는 조정자가 존재하고, 이 조정자는 트랜잭션 요청이 들어왔을 때 2단계를 거쳐 트랜잭션을 담당합니다.
주로 RDBMS 에서 기능을 제공합니다.
Prepare
DB 에게 데이터를 저장할 수 있는 상태인지 묻는 과정입니다.
메시지를 받은 DB에서는 Commit 작업을 위한 준비를 진행합니다. 이후 데이터를 영속할 수 있는 준비가 되면 조정자에게 준비가 되었음을 알립니다.
Commit
조정자가 연관된 DB에게 데이터를 저장하라는 메시지를 송신하며, 수신받은 DB에서는 각자 DB 데이터를 영속화합니다.
모든 DB에서 트랜잭션 터리가 완료되면 전체 트랜잭션을 종료합니다. 만일 Commit이 하나라도 실패하면 모든 DB에게 Rollback을 요구합니다.
단점
각 DB간의 분산 트랜잭션을 지원해야만 적용이 가능합니다. 하지만 NoSQL 에는 지원하지않고, 함께 사용되는 DB가 동일 제품군이어야합니다.
또한 하나의 API 엔드포인트를 통해 서비스 요청이 들어오고, 내부적으로 DB가 분산되어있을 때 사용됩니다.
하지만 MSA 환경에서는 각기 다른 APP에서 API 간 통신을 통해 서비스 요청이 이루어지기 때문에 구현이 쉽지않습니다.
Saga Pattern
Saga 패턴은 트랜잭션의 관리주체가 Application 에 있습니다. App 이 분산 되어있을 때, 각 App 하위에 존재하는 DB는 Local 트랜잭션 처리만 담당합니다.
Saga 패턴은 전체 데이터가 동시에 영속화되는 것이 아니라 순차적으로 트랜잭션이 이루어집니다. 따라서 Application 은 마지막 트랜잭션이 끝났을 때, 데이터가 완전히 영속되었음을 인지합니다.
이는 데이터의 격리성은 보장해주지 않되, 결과적 일관성은 달성할 수 있습니다. 또한 관리를 Application 에서 하기때문에 다른 DB 제품군으로 구성할 수 있습니다.
1. Choreography-Based-Saga
자신이 보유한 서비스 내 local 트랜잭션을 관리하며, 트랜잭션이 종료되면 종료 이벤트를 발행합니다.
그 다음으로 수행할 트랜잭션이 있다면 App 에서 완료 이벤트를 수신받고 다음작업을 처맇바니다.
이때 Event 는 Kafka 와 같은 메시지 큐를 이용해서 비동기 방식으로 전달할 수 있습니다.
Choreography-Based 에서는 중간에 트랜잭션이 실패하면, 해당 트랜잭션 취소 처리를 실패한 App 에서 보상 이벤트를 발생하도록 하여 Rollback 처리를 시도합니다. 이는 구축하기 쉬운 장점은 있지만, 트랜잭션의 현재상태를 알기 어렵다는 단점이 존재합니다.
2. Orchestration-Based-Saga
말그대로 트랜잭션 처리를 위한 Manager 가 별도로 존재합니다. 트랜잭션에 관여하는 모든 App은 Manager 에 의해 점진적으로 트랜잭션을 수행하며 결과를 Manager에게 전달받습니다. 비지니스 로직상 마지막 트랜잭션이 끝나면 Manager 를 종료하여 전체 트랜잭션 처리를 종료합니다.
이는 서비스간의 복잡성이 줄어들고, 구현 및 테스트가 쉽고, 현재 상태도 Manger 가 알고있다는 장점이 있습니다.
하지만 이를 관리하기 위한 Orchestrator 서비스가 추가되고, 인프라면에서 복잡성이 증가한다는 단점이 존재합니다.
Event Sourcing
어플리케이션(도메인)의 모든 상태변화를 순서대로 이벤트로 보관하여 처리하는 개념입니다. 모든 이벤트를 시간순으로 기록하여 처리하므로 분산환경에서 적절히 대응할 수 있습니다. 또한 각 서비스들은 자신이 필요한 이벤트만을 보고있다가 최신의 상태를 유지하면 됩니다.
이런 Event Sourcing 이 완벽하게 동작하려면 모든 이벤트는 기록되어야하고, 이벤트를 받지 못하는 서비스가 없도록 재전송이 가능해야합니다.
평소 우리는 어떠한 로직을 처리하고 그 결과값만 저장해왔습니다. 하지만 이벤트 소싱은 순차적으로 발생하는 이벤트 모두를 저장합니다.
따라서 이벤트 소싱에는 Update, Delete 의 개념이 없고, "~~을 삭제한다" 를 추가하는 개념만이 존재합니다.
이 방식은 사용자의 추가 및 삭제행위가 많아질수록 부하가 오게됩니다. 이를 위해 스냅샷이라는 개념을 도입합니다.
스냅샷으로 N번째 이벤트가 발생했을때의 결과를 저장하게되면 부하를 줄일 수 있습니다.
"2번 책만 주문한 사용자 리스트를 주세요"
위의 말은 이벤트를 모두 재생해서 2번 책만 주문한 사용자 리스트를 반환해야하는데, 오래걸릴것 같은 느낌을 줍니다.
따라서 아래에서 소개할 CQRS 와 궁합이 좋습니다.
CQRS 는 상태를 변경하는 Command 와 조회를 담당하는 Query 를 시스템단위로 분리 시키기 때문입니다.
따라서 이벤트 소싱을 하게되면 이벤트가 저장되기만 하니 쓰기성능에 좋고, 조회 성능은 CQRS 덕분에 효율이 좋습니다.
1. 상태를 저장한 후에 로그를 기록하는것이 아니고, 어떠한 변화를 나타내는 이벤트를 저장소에 기록하고 일련의 이벤트를 재생해서 상태를 만들어내는 것입니다.
2. Command 를 받으면 그 결과로 이벤트를 발생시키고, 그것을 이벤트 저장소에 저장, 이벤트 핸들러를 통해 상태로 반환합니다.
3. 도메인 오브젝트 하나당 도메인 이벤트 스트림이 존재합니다.
4. 조회에는 스냅샷이 있으며, 이 스냅샷은 추가되는것이 아닌 갱신하는것입니다. 이것을 롤링 스냅샷이라 부릅니다.
CQRS
Create, Update, Delete 와 Read 의 책임을 분리하는것입니다.
앞서 설명한 Event Sourcing을 연계시켜 생각해보면 CUD 는 상태변경되는 경우만 발생하고 그 경우만 기록하면 됩니다.
단순히 Read 인 경우는 데이터의 정보를 변경하지 않기 때문에 기능을 분리하여, 손쉽게 Event Sourcing Pattern 을 적용할 수 있습니다.
또한 Read 만 하게만든 데이터베이스는 현재상태의 스냅샷을 생성하고, 이를 통해 조회속도를 손쉽게 개선할 수 있습니다.
하지만 이렇게 데이터베이스를 분리하게되면 데이터 일관성이 깨지는 문제가 발생하는데 CUD 를하는 데이터베이스에서 일관성을 맞춰줘야합니다.
API GateWay
MSA 는 큰 서비스를 잘게 쪼개어 개발/운영하기에 서비스가 많아지면 다음과 같은 문제점이 생깁니다.
- 각각의 서비스마다 인증/인가 등 공통괸 로직을 구현해야하는 번거로움이 생긴다.
- 수많은 API 호출기록을 기록하고 관리하기 어렵다.
- 클라이어트에서 여러 마이크로 서비스를 호출해야한다.
- 내부의 비지니스 로직이 드러나게 되어 보안에 취약하다.
따라서 이러한 단점을 보완하기위해 API GateWay를 사용할 수 있습니다.
1. 인증 및 인가
위에서 언급했듯 수많은 서비스에서 인증, 인가처리를 해야하는 것을 API GateWay 에서 통합으로 관리할 수 있습니다.
따라서 API GateWay 에서 통합으로 관리하여 서비스의 부담을 줄일 수 있습니다.
2. 요청의 단순화
여러 마이크로 서비스를 대상으로 하는 기능을 이용하려면 클라이언트에서 각 서비스에 대한 정보를 알아야하지만 이것을 간소화해줄 수 있습니다.
3. 라우팅 및 로드밸런싱
클라이언트로부터 접수된 메시지에 따라, API 호출을 적절한 서비스에 라우팅할 수 있는 기능이 있습니다. 따라서 부하분산을 관리 할 수 있습니다.
4. 서비스 오케스트레이션
오케스트레이션 로직을 과도하게 넣는 것은 API GateWay 의 부담을 늘리는것으로 성능저하 및 병목현상을 초래할 수 있습니다.
5. 단점
API GateWay 의 Scale-out 적용이 유연하게 일어나지 않을 경우, API GateWay가 병목지점이 될 수 있습니다.
또한 API GateWay 를 사용하는것은 추가적인 네트워크 latency가 증가함을 의미합니다.
Service Mesh
애플리케이션의 서비스 간 모든 통신을 처리하는 소프트웨어 계층입니다. 즉, 마이크로 서비스 간 인프라 사이에서 원활한 통신을 수행하게합니다. 해당 계층은 컨테이너화된 마이크로서비스로 구성됩니다.
애플리케이션이 확장되고 마이크로 서비스의 수가 증가함에 따라 서비스의 성능을 모니터링하기가 점점 어려워지고 있습니다.
Service Mesh 는 서비스 간 연결을 관리하기 위해 모니터링, 로깅, 추적, 트래픽 제어와 같은 기능들을 제공합니다.
이러한 기능은 각 서비스의 코드와 독립적이므로 네트워크 경계를 넘어 여러 서비스 관리 시스템에서 작동할 수 있습니다.
Service Mesh 는 분산 애플리케이션 내에서 복잡한 서비스 간 통신을 처리하는 중앙 집중식 전용 인프라 계층을 제공합니다. Service Mesh 는 다음과같은 이점을 제공할 수 있습니다.
1. 서비스 검색 : 서비스 레지스트리를 사용하여 Mesh 내의 모든 서비스를 동적으로 검색하고 추적할 수 있습니다.
2. 로드 밸런싱 : 다양한 알고리즘을 사용하여 요청을 여러 서비스 인스턴스에 지능적으로 분산합니다. 이는 리소스 활용도를 높이고, 고가용성 및 확장성을 보장합니다.
3. 트래픽 관리 : 라우팅 및 트래픽 동작을 세밀하게 제어할 수 있는 기능을 할 수 있습니다.
Service Mesh 는 서비스와 클라이언트 사이에서 미들웨어의 특성을 갖습니다.
서비스 등록 및 검색 (Service Registery & Discovery)
서비스를 새롭게 생성한다면 해당 서비스에 대한 정보를 Service Mesh 에 존재하는 Discovery 서버에 등록합니다.
각각의 마이크로 서비스들의 IP 정보들을 일일이 등록하지 않더라도 Discovery 서버가 관리하게 됩니다.
API GateWay vs Service Mesh
Service Mesh 와 비슷한 개념을 API GateWay에서 들어본것 같습니다.
유사하게도 라우팅, 인증과 인가, 모니터링, 서비스등록 및 검색을 수행합니다.
하지만 API GateWay 는 Client - Server 이고, Service Mesh 는 Server - Server 입니다.
Sidecar Pattern
애플리케이션 컨테이너와 독립적으로 동작하는 별도의 컨테이너를 옆에 붙이는 패턴을 뜻합니다.
이렇게 붙힌 컨테이너는 상위 애플리케이션에 대해 지원기능을 제공할 수 있습니다.
Spring Cloud Netflix Eureka
넷플릭스에서 MSA를 위해 Spring Cloud 에 제공해준 오픈소스입니다.
MSA에서 회원, 상품, 주문 등 각각의 서비스들에 대한 정보를 저장하여 외부에서 서비스호출 시 그에맞는 서비스 서비로 전달해주는 미들웨어입니다.
Service Discovery
위에서 잠깐 언급한대로 서비스의 IP, Port 를 저장, 관리하는 것입니다.
Client - Side Discovery
Client 서비스가 Service Registry 에서 서비스의 위치를 찾아 로출하는 방식입니다.
대표적으로는 Netflix Eureka 가 있습니다.
서비스 인스턴스의 위치는 생성될 때 Registry 에 등록되고, 종료될 때 삭제됩니다.
장점
- 클라이언트가 사용가능한 서비스를 알고있어 서비스 별로 개별적인 로드밸런싱 방법을 선택할 수 있습니다.
단점
- Client 와 Registry 간의 의존성이 발생합니다.
Server - Side Discovery
로드밸런서가 Service Registry 에 서비스 네트워크 주소를 질의한 후 사용가능한 서비스로 요청을 라우팅합니다.
대표적으로 AWS Elastic Load Balancer 가 있습니다.
등록 및 삭제는 Client - Side 와 동일하게 동작합니다.
장점
- Client 는 Discovery 로직을 구현할 필요가 없습니다.
단점
- 로드 밸런서는 배포환경에 구축되어야 합니다.
- Service Discovery 가 죽는다면 전체 시스템이 동작하지 않습니다.
Message Queue
말 그대로 메시지(이벤트) 를 담은 Queue 입니다.
규모가 커지게되고 MSA 를 적용하면서 서로 통신하고, 데이터를 교환하기 위한 방법이 필요해졌습니다.
그래서 Producer - Consumer 구조를 사용하게 되었고, 대표적으로 RabbitMQ 와 Kafka가 있습니다.
위의 그림처럼 Producer 가 이벤트를 생성하면 Broker 가 어떤 Queue 에 발송할지 결정하는 Exchange 단계를 거칩니다.
이렇게 Queue 에 들어간 이벤트는 Consumer 가 미리 정해둔 한계점까지 이벤트를 가져가게됩니다.
하지만 위와같은 메시지 브로커형태는 Consumer와 Broker 사이의 결합력이 높아지게되면서 추후 트래픽이 증가하여도 수평적으로 확장하는데 어려움이 있습니다.
또한 이벤트가 잘 전달되었다고 판단할 경우 이벤트를 삭제하기 때문에 이를 다시 재생할 수 없습니다.
이벤트 스트리밍 플랫폼
위와 같은 단점을 극복하기 위해 나온 플랫폼입니다.
브로커가 이벤트를 수신하고, 이를 Consumer 가 수신하는 것에는 공통점이 있습니다.
하지만 이벤트 스트리밍 플랫폼은 Topic 이라는 것이 Event Streamer 에 저장됩니다.
Producer 가 이벤트를 생성하면, Topic 이라고 불리는 이벤트의 로그들을 Streamer 에 순서대로 기록하게됩니다.
또한 Topic 을 Consumer 가 가져간 후에도 Event Stream 에서 계속 유지하기 때문에 오류수정이 필요하거나 앱을 리빌드 하는 등의 상황에서 이벤트를 다시 재생시킬 수 있습니다.
이것은 좀 더 유연하고 느슨한 결합을 의미합니다.
굳이 사용 해야하는 이유
아래 서비스를 예시로 들어보겠습니다. 서비스의 사용자가 많아져 인스턴스를 Scale-out 하게되고, 다음의 구조가 되었습니다.
여기서 데이터를 분산되어 저장하기에 데이터 동기화에대한 문제가 발생합니다. (여러개의 인스턴스가 하나의 데이터베이스를 공유하지 않는다는 가정이 존재합니다.)
인스턴스 각각의 데이터베이스를 운영하여 큐잉서버에 구독하여 문제를 해결할 수 있습니다.
Message Queue 의 특징
- Event 를 Queue에 보냈다면 Event가 손실되었는지 Consumer 에게 전달되었는지 확인할 수 있습니다.
- built in으로 중복 Event를 탐지, 제거하는 기능을 넣을 수 있습니다.
- Message Queue 는 다음 2가지 유형이 있습니다.
- 메시지 순서가 보장되지 않을 수도 있지만 높은 처리량을 제공할 수 있는 방법이 존재합니다.
- ex) 이미지 크기 조정, 사용자 가입정보 처리
- 메시지 순서가 보장되지만 낮은 처리량을 제공할 수 있는 방법이 존재합니다.
- ex) 재고조정, 계좌 거래
- 메시지 순서가 보장되지 않을 수도 있지만 높은 처리량을 제공할 수 있는 방법이 존재합니다.
- Message Queue 내부에서 우선순위를 할당하여 우선순위가 더 높은 Event 가 먼저 처리되도록 할 수 있습니다.
- 갑작스럽게 급증하는 트래픽에 대하여 Consumer를 보호 할 수 있습니다.
- Producer 와 Consumer의 결합도가 낮아집니다.
- 하나의 Consumer 가 실패하더라도 Queue는 다른 Consumer에게 Event를 전달합니다.
- Consumer 가 죽어도 Event는 Queue 에 남아있게 됩니다.
- DLQ 라는 특수 Queue 가 있어 전달되거나 처리될 수 없는 Event를 보관합니다.
- Consumer 가 Event 를 사용할 때 Queue는 다른 Consumer 가 사용할 수 없도록 Lock 을 걸 수있습니다.
- 형식이 잘 못되었거나 예상하지 않은 정보가 포함되어 있어 처리할 수 없는 Event를 Poison Message 라고 부릅니다.
Poison Message
1. Consumer가 예외를 발생시킵니다.
2. Event는 다시 Queue 로 돌아갑니다.
3. 또 다른 Consumer 가 가져가서 예외를 발생시키고, Queue로 돌려보냅니다.
4. 이 과정이 무한히 발생합니다.
따라서 Poison Message 를 빠르게 감지하고 제거하는 것이 중요합니다.
'Infra' 카테고리의 다른 글
[Infra] Docker Network 개념 (0) | 2024.08.25 |
---|