본문 바로가기
web/SpringBoot

[MSA] Backing service - MOM

by 뽀리님 2023. 11. 28.

 

✔  Backing service

Backing Service란, 어플리케이션이 실행되는 가운데 네트워크를 통해서 사용할 수 있는 모든 서비스를 말하며 My SQL과 같은 데이터베이스, 캐쉬 시스템, SMTP 서비스 등 어플리케이션과 통신하는 attached Resource들을 지칭하는 포괄적인 개념

 

✔  마이크로서비스 Backing service의 특징

마이크로 서비스에서의 Backing service 메세지큐를 활용한 비동기 통신 패턴을 많이 사용한다.

MSA 특징 하나는 하나의 Micro Service 이벤트(장애 발생, 트래픽 증가, 소스 반영 ) 발생할 경우, Micro Service 오케스트레이션이 진행되며, 마이크로서비스의 신규 생성, 재생성, 서비스 인스턴스의 삭제 등의 작업이 빈번하게 이루어진다는 것이다.

 

 

MOM (Message Oriented Middleware)

이름에서부터 역할을 알 수 있듯, 어플리케이션들의 메시지를 중간에서 관리해주는 시스템이다.

  • 여러 클라이언트 시스템간에 메시지 통신을 중간에서 관리해줌으로써 클라이언트 시스템간의 종속성 및 결속성을 낮춰준다. 메시지를 보내는 이는 받는 이의 주소를 몰라도 보낼 수 있게 된다.
  • 주고 받는 메시지를 DB 등에 기록하는 등 백업 할 수 있기에 안정성을 높여준다.
  • 라우팅 규칙을 활용하여 하나의 메시지로도 특정 여러 클라이언트가 받을 수 있도록 지원한다.

물론, 단점도 존재함!

  • 메시지 전체를 관리하는 시스템이 따로 필요하다. 중앙에서 메시지를 관리할 수 있는 주체가 필요하기 때문에 이에 대한 도입 비용, 운영 비용 등 고려해야한다.
  • 당연히 전체적인 시스템 구조가 복잡해질 수 있다. 송수신자가 직접 통신하는게 아니므로 다수의 서버가 MOM을 활용하게 되면 구조가 전체적으로 복잡해지고, 이에 대한 오버헤드가 발생할 수 있다.

- MOM 표준 및 프로토콜

  • Java 에서 지원하는 표준 API 인 JMS(Java Messaging System)
  • 미들웨어 브로커 간 메시지 교환을 위한 Application Layer 표준 프로토콜인 AMQP(Advanced Message Queue Protocol)

종류로는 ActiveMQ,RabbitMQ,Apache Kafka,Amazon SQS 등이 있다.

 

앞에서 말했듯 MOM은 이론, 개념, 설계적인 방향성을 제시하고 있다고 보면 된다. 직접적인 구현체는 아니다.

 

✔ Message Broker

참조 https://dev-jwblog.tistory.com/140

 

MOM을  구현한 구현체가 메시지브로커다.

 

...근데 왜 사용하는 걸까?

위에서 MOM의 개념과 장단점, 그리고 이를 활용한 프로토콜이라던지, 구현체들을 살펴보았는디...

이런 메시징 브로커... 도대체 왜 사용하는 걸까??? 

 

사용하는 이유는 3가지로 나누어 말할 수 있다.

  • 서비스(어플리케이션) 간의 의존성 제거
  • 메시지 처리 시점
  • 다양하고 유연한 통신

 

 서비스 간의 의존성 제거

참조 https://binux.tistory.com/74

 

위와 같이 간단히 송신자와 수신자가  특정 업무를 위해 메시지를 전송하고 있다고 하자.

사용자가 적은 단순한 서비스라면 위의 구조로도 잘 작동할 것이다. 송신자가 직접 수신자의 주소 정보를 알고, 해당 주소로 메시지를 보내면 되니깐말이다.

 

참조 https://binux.tistory.com/74

하지만 사용자가 많아져 서버를 늘리는 상황(Scale-out) 되었다. 다음 그림처럼 송신 수신 서버를 늘렸다고 하자. 이런 경우에는 송신측에서 수신측으로 메시지를 전달할 있을까?

 

뭐... 각 송신측 서버에 접속해서 수신측 주소를 입력해주거나 하면 운영이 ... 뭐 될 수야 있겠지

 

하지만 매우 수고로운 일이면서, Cloud platform을 사용한다면 수신측의 주소는 서버가 늘어났다 줄어드는 상황이 많아 사람이 할 수 있는 일이 아닐 수 있다(인력의 운영비용이 엄청나게 증가할듯.....) 이러한 문제를 서비스 검색이 불가능하다고 할 수 있다.

 

 

, 다시 예제상황으로 돌아와서.... 만약 수신 문제가 있어 정상적으로 동작하지 않는 상황이라면 어떨까?

참조 https://binux.tistory.com/74

당연히도 송신 측에서 오류가 발생한다. 제대로 된 전송이 불가능하기 때문이다.

 

이 부분에서 동기(Synchronous) 방식으로 메시지 전송을 했다면 더 큰 오류가 발생할 수 있다. 송신 측은 Response를 받을 때까지 대기를 하게 될 텐데 수신 측 서버가 정상적으로 동작하지 않아 정해 둔 timeout 시간까지 기다리기만 하게 될 것이다.

이런 상황 속에서 계속해서 송신 측에 request가 들어오게 되면 요청들이 쌓이게 되고 송신 측 서버마저 문제가 생기게 된다.

  • 송신 측 서버가 문제가 생기면 송신 측 서버에 Request를 보내는 서버들도 또 비슷한 문제가 발생할 수 있다. 이를 장애 전파라고 한다. 이러한 장애전파를 막기 위해선 Circuit breaker, fall-back 등을 활용해야 한다.

 

비동기(Asynchronous) 방식으로 메시지를 전송했다면 위와 같은 문제는 없을지라도 메시지가 수신 측에 정상적으로 전달되지 않아 메시지가 없어지는 문제. 즉, 메시지의 전달이 보장되지 않는 문제가 발생하게 된다. 그렇기에 추후 수신 측 서버가 살아나더라도 죽었을 당시의 메시지들은 다 사라진 후일 것이다.

 

이러한 문제를 봐서 아시겠지만, 메시지 브로커 없이는 송신 및 수신 서버가 모두 다 정상적으로 작동하는 상태여야만 메시지 전송이 정상적으로 동작하게 된다.

 

 

이 뿐 아니라 문제가 없는 상황에서도 메시지 전달은 보장되어야만 한다. 이러한 메시지 보장에 대한 메커니즘을 전달 보장 메커니즘(Guaranteed delivery mechanism)이라고 하는데,

메시지 브로커가 없는 상황에서는 송신 또는 수신측에 직접 구현을 해야할 것이다. 이러한 메커니즘을 구현하기는 쉽지 않을 뿐더러 특정 어플리케이션 관계마다 구현해주기 더 쉽지 않을것이다.

 

앞서 살펴 본 문제들을 정리하자면 아래와 같다.

  • 서버의 수가 유동적이면 정상적으로 동작이 어려움 (서비스 검색 불가상황)
  • 송신 및 수신 서버가 모두 정상적으로 동작해야만 한다는 것
  • 전달 보장 메커니즘이 없거나 구현하기 어려움

이러한 문제들이 서비스 간의 의존하고 있기에 발생하고 있다고 생각했고, 이를 어플리케이션의 의존성으로 인한 문제라고 이해했다.

 

메시지 브로커는 이러한 어플리케이션 의존성을 제거해줌으로써 앞선 문제들을 해결해주고 있다.

참조 https://binux.tistory.com/74

 

 

✓ 서버 수가 유동적이어도 정상적으로 동작
  • 송신 측 서버가 늘어나도 메시지 브로커의 주소만 알고 있다면, 메시지를 보내는데는 문제가 없다.
  • 수신 측 서버가 늘어나도 메시지 브로커의 주소만 알고 있다면, 메시지를 받아오는데는 문제가 없다.

 

✓ 수신 측 서버가 문제가 생겨도 정상적으로 동작
  • 송신 측이 메시지를 보내면, Message Broker는 메시지를 받아 큐에 저장해놓고 수신 측이 받아가길 기다린다.
  • 수신 측 서버에 문제가 생겨 이 메시지를 받아가지 못하더라도 메시지 브로커에서는 정해둔 설정 값마다 다르겠지만 메시지를 유지하고 있다.
  • 수신 측 서버가 정상적으로 돌아오면 그 유지해 두었던 메시지를 받아 올 수 있어 문제가 생겼던 기간의 메시지들도 받을 수 있다.

 

✓ 그럼, 만약 메시지 브로커 자체가 죽으면 어떻게 될까?
  • 브로커 자체가 죽는다고 송신 및 수신 측 서버에 부담이 가지 않는다. 메시지 전송의 출구만 없어질 뿐 그 자체의 서버 역할을 하고 있을 것입니다. But 하지만 당연히도 메시지 전송 기능 자체에는 문제가 발생할 수 있다.
  • 하지만 메시지 브로커 서버는 일반 서버와 달리 메시지 전송과 메시지 큐 부분에만 집중을 둔 서버로서 고가용성을 목표로 만들어져있어 서버가 이상이 있을 위험이 더 적다고 한다.

또한 메시지 브로커들은 보통 전달 보장 메커니즘이 구현되어 있다!!

 

 ✔  메시지 처리 시점

앞서 이야기중 "송신자가 메시지를 많이 보내 수신자가 터진다" 라는 말이 있었다.

그럼, 메시지 브로커가 있으면 이를 해결 있는 걸까? 메시지 브로커는 단순히 메시지를 저장했다가 바로 수신측에 주는 아닌가? 라는 궁금증이 생겼다.

 

참조 https://binux.tistory.com/74

 

해당 궁금증대로라면 사실 메시지 브로커를 사용할 필요가 없다.

 

메시지 브로커에서는 수신 측에서 원하는 시점에 메시지를 가져갈 수 있도록 (처리할 수 있도록) 지원하고 있다. 여기서 원하는 시점이란 유동적일 수도 있겠지만 보통 수신 측이 메시지를 처리할 수 있는 시점을 의미한다. 이러한 기능을 '메시지 버퍼링'이라고 부르기도 합니다.

다시 말해서, 메시지 브로커는 수신 측에서 처리 가능한 시점까지 메시지 버퍼링 있는 것이다.

 

 

 

다양하고 유연한 통신

 

참조 https://binux.tistory.com/74

특정 이벤트가 발생하면 수신 측에 메시지를 전달하는 구조가 있다.

 상황 속에서 기획  개발 구조 수정으로 송신 측의 메시지를 받을 수신  서버가 늘어나게 되었다.

 

수신 측 서버의 개수는 2개일 수도, 3개일 수도, 많게는 10개일 수도 있다. 이러면 송신 측에선 어떻게 해야할까?

 

만약 현재의 구조대로라면 내부 코드에서 수신 측 주소로 직접 요청하는 식으로 구현을 해두어야 할 것 인데.....

정말 많은 불편함이 있을 것이며, 어느때(서버의 대수가 유동적으로 변경되는 시점)가 되면 이게 불가능한 시점이 올 수도 있다.

 

 

이런 경우, 메시지 브로커를 활용한다면 문제를 해결할 수 있다.

메시지 브로커에서는 다양한 메시지 채널 방식을 지원하고 있고, 점대점(point-to-point) 및 발행-구독(pub-sub) 채널은 보통 지원을 하고 있어 위에서 설명하던 구조를 쉽게 구현할 수 있다.

 

위에서 살펴 보았듯 기존 서버구조라면 해결하기 어렵거나, 해결하지 못하는 문제들을 '메시지 브로커'를 활용해 해결할 수 있었다.

 

 

메시지 브로커의 단점

당연히도 단점이 존재함

 

1. Bottleneck point(병목현상 지점)이 될 수 있다.

  • 많은 고객 수로 인해 서버들이 확장(Scale-out)하는 상황에서 만약 메시지 브로커는 기존 수 그대로 남아있다면, 늘어난 고객 수 및 서버 수의 요청을 같은 수의 메시지 브로커로 감당하는 것이므로 당연히도 병목현상이 발생할 수 있다. 메시지 큐에 메시지가 쌓이면서 요청을 처리하는 속도가 느려질 수 있다.
  • 하지만 최근 많은 메시지 브로커들이 확장이 가능한 구조로 설계되어 이러한 문제는 적어지고 있다. 서버 수의 증가에 따라 메시지 브로커의 수도 확장해나감으로서 문제를 해결할 수 있다.

2. 메시지 브로커도 죽을 수 있다.

  • 앞서 말씀드렸듯 메시지 브로커도 죽을 수 있다. 메시지 브로커가 죽게되면 송신 및 수신 측의 커뮤니케이션이 불가능한 상태가 되므로 SPOF(Single Point Of Failure)가 될 수 있다.
  • 이러한 메시지 브로커의 문제를 이해해 서버 구조를 설계해야하며, 다행히도 요즘 메시지 브로커는 고가용성이 보장되도록 설계되었다.

3. 메시지 브로커 서버를 운영해야 한다.

  • 당연한 사실이겠지만, 일반 서버와 별개로 메시지 브로커(서버)를 운영해야 한다. 정상적으로 동작하는지 확인하거나, 알람 기능을 추가해 생성해야 한다거나, 버전을 꾸준히 업데이트 해줘야한다거나, 버전 업그레이드로 인한 문제가 발생하진 않는지 확인하는 등 여러 이슈들을 확인하고 해결해나가야 한다.
  • 메시지 브로커도 관리 및 운영을 해줘야하므로 운영비용이 증가하는 단점이 있다.

 

 

RebbitMQ 와 Kafka

  1. kafka 이벤트브로커(pub/sub) 방식RabbitMQ 메시지 브로커 방식
    kafka
     pub/sub방식은 생산자 중심적인 설계로 구성. 생성자가 원하는 메시지를 게시할 있도록 하는 메시지 배포 패턴으로 진행
    RabbitMQ
    의 메시지브로커 방식은 브로커 중심적인 설계로 구성. 지정된 수신인에게 메시지를 확인, 라우팅, 저장 배달하는 역할을 수행하며 보장되는 메시지 전달에 초점
  2. 전달된 메시지에 대한 휘발성
    RabbitMQ
     queue 저장되어 있던 메시지에 대해 Event Consumer가져가게 되면 queue에서 해당 메시지를 삭제한다.
    하지만kafka 생성자로부터 메시지가 들어오면 해당 메시지를 topic으로 분류하고 이를 event streamer 저장한다. , 수신인이 특정 topic 대한 메시지를 가져가더라도 event streamer 해당 topic 계속 유지하기 때문에 특정 상황이 발생하더라도 재생이 가능하다.
  3. 용도의 차이
    kafka
    클러스터를 통해 병렬처리가 주요 차별점인 만큼 방대한 양의 데이터를 처리할 , 장점이 부각된다.
    RabbitMQ
    데이터 처리보단 Manage UI 제공하는 만큼 관리적인 측면이나, 다양한 기능 구현을 위한 서비스를 구축할 , 장점이 부각된다.

Kafka 대용량의 분산 로그 트래픽을 처리한다는 점에서 유리하다면, RabbitMQ 높은 처리량보다는 지정된 수신인에게 원하는 방식으로 메시징을 신뢰성 있게 전달하는데 초점이 맞춰져 있다.

그래서 RabbitMQ 경우에는 대용량 트래픽에는 조금 불리한 점이 있는 대신 익스체인지 타입이나 라우팅 정책에 따라서 동작 방식을 선택할 수가 있는 장점이 있다. 

 

 

 

결론 

사용자가 많은 대용량 서비스에서 서버가 확장되어 늘어나거나 장애가 생겨도 

메시지브로커로 원하는시점에 데이터를 주고 받을 있다.

용도에 따라 다르겠지만 대용량분산 트래픽이라면 카프카를, 단순 메시지브로커면 래빗엠큐를 쓰지만 특히 지정된 수신인에게 신뢰성있는 메시징을 보장해야 하는 경우면 래빗엠큐를 써야한다.

 

 

참고

https://bcho.tistory.com/1252

https://foot-develop.tistory.com/19

https://binux.tistory.com/74

https://4sii.tistory.com/491?category=1105026

https://binux.tistory.com/74