기록하는 습관

[개념/적용] MQ에 대한 개념 및 적용 본문

스마일게이트 인턴 - Dev Camp

[개념/적용] MQ에 대한 개념 및 적용

로그뉴 2021. 1. 26. 01:17

실시간 친밀도 랭킹

 

** MQ를 프로젝트에 도입하게 된 이유

친밀도 랭킹을 계산하는 것은 사용자에게 즉각적으로 실시간 응답이 되지 않아도 되는 프로세스로,

백엔드에서 비동기적으로 처리할 수 있는 배치 프로세스이다. 처음엔 하루 주기의 배치 프로세스로 작업을 하려고 했지만 큐를 활용해 최대한 배치 작업들을 실시간 업데이트를 하며 효율적으로 처리하고자 했다.

 

좋아요/댓글 프로세스에서 score 계산까지 같이 하는 것이 가장 좋지만, 사용자가 많아지고 좋아요/댓글 요청이 많아질수록 Ranking 계산 때문에 delay가 생길 수 있으므로 성능 상 문제가 생길 수 있다.

따라서, MQ를 도입하여 좋아요, 댓글 계산 요청을 MQ에 보내 사용자가 바로 다음 액션을 취할 수 있도록 하고 계산 처리는 MQ에서 꺼내 별도로 진행하도록 했다.

 

** 메시지 큐를 사용하는 이유

  1. 어플리케이션과 데이터베이스의 동기적 직접통신 구조 (기존)

: 어플리케이션과 DB가 강하게 결합되어 있어 어플리케이션의 요청&응답 과정에서

DB 서버로의 요청&응답 모두 완료되어야 응답이 가능하다.

 

따라서 다음과같은 문제가 발생할 수 있다.

 

-  DB의 응답 시간이 길어진다면 어플리케이션 또한 그만큼 응답시간이 길어진다.

-  DB 장애시 어플리케이션이 동작하지 못한다.

-  어플리케이션 입장에서 감당할 수있는 요청 수가 DB에서는 감당 불가능하다면,

성능저하나 장애가 발생할 수 있다.

 

    2. MQ를 통한 어플리케이션과 데이터베이스의 통신

: 어플리케이션은 큐에 요청을 쌓고, DB는 큐의 요청을 꺼내 처리한다.

- 어플리케이션은 큐에 요청을 보내고, DB의 응답을 기다리지 않고 응답을 보낼 수 있다.

-  DB 장애 발생시에도 어플리케이션은 독립적으로 동작이 가능하다.

- 어플리케이션과 DB 사이의 통신을 처리량에 따라 제어할 수 있다.

 

 

** 메시지 큐의 장점

 - 비동기(Asynchronous): Queue에 넣기 때문에 나중에 처리할 수 있습니다.

 - 비동조(Decoupling): 애츨리케이션과 분리할 수 있습니다.

 - 탄력성(Resilience): 일부가 실패 시 전체에 영향을 받지 않습니다.

 - 과잉(Redundancy): 실패할 경우 재실행 가능합니다.

 - 보증(Guarantees): 작업이 처리된걸 확인할 수 있습니다.

 - 확장성(Scalable): 다수의 프로세스들이 큐에 메시지를 보낼 수 있습니다.

 

 

** 메시지 큐 종류 (대표적)

Apache Kafka

RabbitMQ

 - 구독방식의 비동기식 구성

 - 고성능 고가용성 

 - 분산처리에 효과적으로 설계 됨

 - 생산자 중심의 설계 

 - 범용 메세징 시스템에서 제공되는 다양한 기능은 제공되지 않음.

 - 100k/sec 처리를 보장

, 처리량이 많은 분산 메시징 시스템에 적합함.

 - 구성이 쉽다

 - 유연한 라우팅이 가능하면 관리 UI 가 편리하다

 - 제품 성숙도가 높다

 - 개방형 프로토콜을 위한 AMQP 를 구현위해 개발 

 - 필요에 따라 동기/비동기식이 가능함 

 - 소비자중심의 설계

 - 20k/sec 처리를 보장

, 신뢰성 있는 메시지 브로커가 필요한 경우 적합함.

 

  • 차이점에 대한 결론:

많은 양의 데이터를 처리해야 한다면 기능이 좀 부족한 걸 감수하고 나서라도 kafka를 쓰는 것이고,

데이터 처리보단 관리적 측면이나 다양한 기능 구현을 위한 서비스를 구축하기 위해선 RabbitMQ 가 좋다.

기존 메시징 시스템에서는 broker가 consumer에게 메시지를 push해주는 방식인데 반해, Kafka는 consumer가 broker로부터 직접 메시지를 가지고 가는 pull 방식으로 동작한다.

→ Paprika에선 실시간 랭킹 업데이트를 위해 많은 양의 데이터를 주고 받아 처리하는 것이 아니라, 비동기 적으로 랭킹 업데이트를 진행하고자 하기 때문에 RabbitMQ 채택.

    Kafka는 데이터 처리에 강하고 구현도 상대적으로 어려운 이유로 채택 x.

 

 

** RabbitMQ

  1. 용어/개념 정리
  • Producer : 메세지를 생성하고 발송하는 주체
  • Queue: Producer들이 발송한 메세지들이 Consumer가 소비하기 전까지 보관되는 장소
  • Consumer: 메세지를 수신하는 주체
  • Subscribe: Consumer가 메세지를 수신하기 위해 Queue를 실시간으로 리스닝하도록 만듬.
  • Exchange: Producer들에게서 전달받은 메세지들을 어떤 Queue들에게 발송할지를 결정하는 객체

Type

특징

Direct

Routing key가 정확히 일치하는 Queue에 메세지 전송

unicast

Topic

Routing key 패턴이 일치하는 Queue에 메세지 전송

multicast

Headers

[key:value]로 이루어진 header 값을 기준으로 일치하는 Queue에 메세지 전송

Multicast

Fanout

해당 Exchange에 등록된 모든 Queue에 메세지 전송

broadcast

  • Binding: Exchange에게 메세지를 라우팅 할 규칙을 지정하는 행위

 

    2. 구조 정리

  • Work Queue : 워커에게 하나씩 균등히 나누어 준다
  • Publish/Subscribe: 모든 소비자에게 다 준다.

* 참고: Work Queue vs Pub/Sub

Work Queue는 항상 하나의 메세지는 하나의 컨슈머만 받을수있다는 것,

Pub/Sub 패턴에서는 하나의 메세지를 여러 워커가 받을수있다라는 점.

  • Routing: 원하는 것을 가져가게 한다.
  • Topics: 특정 패턴에 기반해 나누어 가진다.
  • RPC

 

** 구조 선택(Work Queue vs Direct)

Work Queue: 하나의 작업 큐를 두어 Consumer 개수가 늘어나면 개수에 맞춰 Round-robin(균등분배)하게 움직인다.

Direct: queue를 여러 개 두어 like, comment, follow별로 라우팅을 진행해 각각 다른 Consumer을 두고 진행한다.

 

** 의문?

Direct 방식으로 해서 다수의 큐를 두고 각각 Consumer가 처리하게 하는게 나은지, Work Queue 방식으로 하나의 큐에 많은 Consumer를 두어 처리하게 하는게 나은지.

그리고 Direct에도 또 Consumer을 둘 경우에는 결국 Direct가 더 좋은 것이 아닌가..?

 

** Work Queue를 선택한 이유

Round-robin은 응답시간이 짧아 실시간 시스템에 유리하다는 장점이 있어 Work Queue를 선택하여 다수의 Consumer를 두기로 결정.

 

** Acknowlegement(승인) 처리

승인 처리는 매우 중요하다. 승인을 안해주면 consumer가 멈추게 되기 때이다. 또한 consumer가 승인을 보내지 않고 커넥션을 끊은 후 다시 접속하면 데이터는 복구 되기 위해 다시 큐에 적재되는데 이 경우 이미 처리한 데이터를 또 처리하기 때문에 문제가 심각해진다.

noAck ack를 받기 전에 큐에서 메시지를 제거 할건지 여부를 결정하는 것인데, true로 설정하게 되면 DB에 랭킹이 업데이트 되지 않아도 ack를 보내게 될 수 있으므로 false로 설정했다.

 

** Fair dispatch(공평한 분배)

하나의 Queue에 여러 Consumer가 존재할 경우, Queue는 기본적으로 Round-Robin 방식으로 메세지를 분배하지만 완전히 공평하진 않다. 매 홀수는 데이터크기가 크고, 매 짝수는 데이터 크기가 작은 경우 홀수 버퍼에 계속 쌓이게 된다.

  • prefetchCount 사용

: prefetchCount 1일때는 한 번에 1개의 데이터만 처리 가능하므로 아직 ack를 받지 못했으면 그 버퍼에는 메시지 보내지 않음. 따라서 작업을 분산 시킬 수 있다.

현재는 1로 설정하여 최대 허용 가능한 수를 1개로 제한함.

 

** Message Queue  Message 보존

RabbitMQ server가 종료(어떤 이유에서든지)후 재가동하면, 기본적으로 Queue는 모두 제거된다.

이를 막기 위해서는 Queue를 생성할 때, Durable 옵션에 true를 주고 생성해야 하며,

Producer가 메세지를 발송할 때, PERSISTENT_TEXT_PLAIN 옵션을 주어야 메세지가 보존된다.

현재Durable true로 설정해 메시지 큐 서비스가 죽어도 큐가 사라지지 않고 보존되게 함.

Comments