수 많은 우문은 현답을 만든다

메세지 큐 - 1. MQ 비교 및 설치 본문

토이 프로젝트

메세지 큐 - 1. MQ 비교 및 설치

aiden.jo 2024. 11. 6. 15:37

오늘은 대규모 요청에 대해서 Timeout이 나지 않도록 하기 위해서 메세지 큐를 적용해보고자 한다.

어떤 메세지큐를 써야할까? 카카오 면접을 볼때 어떤 메세지 큐를 왜 선택 했는지에 대한 질문을 받은 기억이 있다.

 

메세지 큐 비교

1. RabbitMQ

  • 프로토콜: AMQP(Advanced Message Queuing Protocol)를 지원하며 다양한 프로토콜을 지원할 수 있습니다.
  • 특징:
    • 고성능, 안정성 및 다양한 메시지 전달 기능(예: 라우팅, 큐의 팬아웃 등).
    • 다양한 플러그인을 통해 기능 확장이 가능.
  • 장점:
    • 멀티 프로토콜 지원.
    • 메시지의 영속성을 유지할 수 있는 기능.
  • 단점:
    • 설정과 관리가 상대적으로 복잡할 수 있음.
    • 고성능 애플리케이션의 경우 성능 최적화가 필요할 수 있음.

2. Apache Kafka

  • 프로토콜: 자체 TCP 기반 프로토콜 사용.
  • 특징:
    • 대규모의 실시간 데이터 스트리밍과 이벤트 스트리밍 처리에 최적화.
    • 분산형 아키텍처로 높은 확장성과 내구성을 제공.
  • 장점:
    • 높은 처리량과 낮은 지연 시간.
    • 이벤트 스트리밍에 특화된 다양한 기능.
  • 단점:
    • 초반 설정 및 관리가 복잡할 수 있으며, 운영 경험이 필요함.
    • 메시지 보존 전략이나 데이터 볼륨 관리가 필요.

3. Amazon SQS (Simple Queue Service)

  • 프로토콜: AWS의 관리형 서비스로 HTTP/HTTPS 기반 API 사용.
  • 특징:
    • 완전 관리형 서비스로 사용자가 서버를 관리할 필요가 없음.
    • 메시지의 자동 삭제, 중복 제거 기능 등 다양한 옵션 제공.
  • 장점:
    • 설정과 사용이 매우 간단하며, 확장성과 안정성 보장.
    • 비용 효율적이고 AWS의 다른 서비스와 쉽게 연동 가능.
  • 단점:
    • 최대 메시지 보존 기간(기본적으로 최대 14일).
    • 특정 기능(예: 복잡한 라우팅)에 한계가 있음.

4. ActiveMQ

  • 프로토콜: AMQP, MQTT, STOMP, OpenWire 등 다수의 프로토콜을 지원.
  • 특징:
    • 다양한 메시지 패턴을 지원하며, JMS(Java Message Service)와 잘 통합됨.
    • 클러스터링과 페일오버 기능이 있음.
  • 장점:
    • 다양한 프로토콜과 언어를 지원하는 유연성.
    • JMS 표준 지원으로 Java 기반 프로젝트에서 많이 사용됨.
  • 단점:
    • 복잡한 설정과 성능 튜닝 필요.
    • RabbitMQ나 Kafka에 비해 대규모 처리 성능이 상대적으로 떨어질 수 있음.

 

메세지 큐 선택

현재 하고자 하는 토이 프로젝트는 15만 request를 수용하는 400TPS 정도의 성능을 내야하는 프로그램이다. 분산처리를 해야하는 대규모 프로젝트는 아니기도 하고 수 일 내에 구현부터 테스트까지 완료해야하는 상황이기 때문에, 초반 러닝커브가 짧고 구현이 쉬운 RabbitMQ를 선택하기로 했다.

 

메세지 큐 설치

우선은 erlang을 설치해야한다. RabbitMQ는 Erlang/OTP(이하 Erlang)로 작성된 애플리케이션이기 때문에 Erlang 런타임 환경이 필요하다. Erlang은 범용 프로그래밍 언어이자 런타임 환경으로, 특히 분산 시스템, 고가용성, 실시간 시스템의 개발에 최적화되어 있다.

 

Windows 설치

https://www.erlang.org/downloads
https://www.rabbitmq.com/docs/install-windows#installer

 

리눅스 설치

sudo apt-get update
sudo apt-get install -y erlang
sudo apt-get install -y rabbitmq-server

 

 

 

메세지 큐 실행

나는 Windows OS를 쓰고 있어서 윈도우 기반으로 작업을 했다.

 

환경 변수 설정

  • 시스템 속성 < 환경 변수 < Path 선택 < 편집 < 새로만들
  • {your own path}를 입력한다 (ex. D:\RabbitMQ\rabbitmq_server-4.0.3\sbin)

RabbitMQ 실행

  • ForeGround 방식 : 시작 < CMD < rabbitmq-server
  • BackGround 방식 : 시작 < rabbitmq Service Start

GUI 플러그인 설치

  • rabbitmq-plugins enable rabbitmq_management

실행에러

혹시 실행중 에러가 난다면 아래 접힌 글을 참고하세요

더보기

C:\Users\user>rabbitmq-server

=INFO REPORT==== 6-Nov-2024::12:16:39.013000 ===
    alarm_handler: {set,{{disk_almost_full,"C:\\"},[]}}
2024-11-06 12:16:39.110000+09:00 [warning] <0.134.0> Using RABBITMQ_ADVANCED_CONFIG_FILE: c:/Users/user/AppData/Roaming/RabbitMQ/advanced.config

2024-11-06 12:16:39.415000+09:00 [error] <0.134.0>
2024-11-06 12:16:39.415000+09:00 [error] <0.134.0> BOOT FAILED
2024-11-06 12:16:39.415000+09:00 [error] <0.134.0> ===========
2024-11-06 12:16:39.415000+09:00 [error] <0.134.0> ERROR: could not bind to distribution port 25672, it is in use by another node: rabbit@DESKTOP-UCGARPL
2024-11-06 12:16:39.415000+09:00 [error] <0.134.0>
BOOT FAILED
===========
ERROR: could not bind to distribution port 25672, it is in use by another node: rabbit@DESKTOP-UCGARPL

2024-11-06 12:16:40.424000+09:00 [error] <0.134.0>     supervisor: {local,rabbit_prelaunch_sup}
2024-11-06 12:16:40.424000+09:00 [error] <0.134.0>     errorContext: start_error
2024-11-06 12:16:40.424000+09:00 [error] <0.134.0>     reason: {dist_port_already_used,25672,"rabbit","DESKTOP-UCGARPL"}
2024-11-06 12:16:40.424000+09:00 [error] <0.134.0>     offender: [{pid,undefined},
2024-11-06 12:16:40.424000+09:00 [error] <0.134.0>                {id,prelaunch},
2024-11-06 12:16:40.424000+09:00 [error] <0.134.0>                {mfargs,{rabbit_prelaunch,run_prelaunch_first_phase,[]}},
2024-11-06 12:16:40.424000+09:00 [error] <0.134.0>                {restart_type,transient},
2024-11-06 12:16:40.424000+09:00 [error] <0.134.0>                {significant,false},
2024-11-06 12:16:40.424000+09:00 [error] <0.134.0>                {shutdown,5000},
2024-11-06 12:16:40.424000+09:00 [error] <0.134.0>                {child_type,worker}]
2024-11-06 12:16:40.424000+09:00 [error] <0.134.0>
2024-11-06 12:16:40.424000+09:00 [notice] <0.45.0> Application rabbitmq_prelaunch exited with reason: {{shutdown,{failed_to_start_child,prelaunch,{dist_port_already_used,25672,"rabbit","DESKTOP-UCGARPL"}}},{rabbit_prelaunch_app,start,[normal,[]]}}
{exit,terminating,[{application_controller,call,2,[{file,"application_controller.erl"},{line,511}]},{application,'-ensure_all_started/3-lc$^0/1-0-',1,[{file,"application.erl"},{line,367}]},{application,ensure_all_started,3,[{file,"application.erl"},{line,367}]},{rabbit,'-start_it/1-fun-0-',1,[{file,"rabbit.erl"},{line,422}]},{timer,tc,2,[{file,"timer.erl"},{line,590}]},{rabbit,start_it,1,[{file,"rabbit.erl"},{line,420}]},{init,start_it,1,[{file,"init.erl"},{line,1546}]},{init,start_em,1,[{file,"init.erl"},{line,1521}]}]}
Runtime terminating during boot (terminating)

Crash dump is being written to: erl_crash.dump...done

 

 

위 내용을 보면 25672 포트가 이미 사용중이라고 나온다.

1) cmd < netstat -aon | findstr 25672

  TCP    0.0.0.0:25672          0.0.0.0:0              LISTENING       12664

2) 작업관리자 < 자세히 탭 < 12664 PID 를 찾아서 종료

 

실행

management 플러그인이 설치된 후에는 rabbitmq를 재시작 해야한다.

rabbitmqctl status
rabbitmqctl stop
rabbitmqctl start

 

위 커맨드를 사용한 후에 브라우저를 켜고 http://localhost:15672 로 접속하면 아래와 같이 접속 되는 것을 확인할 수 있다.

*기본 접속 ID/PW는 guest/guest이다.

 

 

RabbitMQ Server 유저 생성

  • 유저를 추가하는 방법은 두 가지가 있다.
    • RabbitMQ Server 관리 화면 Admin 탭에서 유저 생성
    • CLI에 명령어를 입력하여 유저 생성

여기서는 CLI 환경에서 유저를 생성하는 방법을 정리한다.

  • 유저 생성
    • rabbitmqctl add_user <username> <password>
    rabbitmqctl add_user han han

 

  • 유저 역할 설정
    • rabbitmqctl set_user_tags <username> administrator
    • 유저의 역할에는 administrator, monitoring, policymaker, management, none이 있다.
    rabbitmqctl set_user_tags han administrator

 

  • 유저 확인
    • rabbitmqctl list_users

 

  • 유저 vhost 권한 설정
    • rabbitmqctl set_permissions [-p <vhostpath>] <user> <conf> <write> <read>
    • RabbitMQ에는 Virtual Host라는 개념이 존재하는데, 일단은 Message Queue의 주소로 생각하자.
    rabbitmqctl set_permissions -p / "han" ".*" ".*" ".*"

 

  • 유저 vhost 권한 확인
    • rabbitmqctl list_permissions

RabbitMQ 관리 화면의 Admin 탭을 클릭해서도 진행 과정을 확인할 수 있다.

 

 

구현

기본적으로 RabbitMQ의 소스코드는 아래와 같다.

async def my_api(
    base_date
):
    # 요청 ID 생성
    request_id = str(uuid.uuid4())
    
    # 요청을 큐에 추가
    publish_to_queue(request_id, base_date)
    
    # 요청 상태 저장 (처리 중 상태로 설정)
    redis_client.set(request_id, "Processing")
    
    return {"request_id": request_id, "status": "Request received, processing will begin shortly."}

 


이번 프로젝트에서는 큐를 못쓰게 될 것 같다. 이유를 살펴보자.

1) 위 코드를 보면, 동시에 10만개 요청이 들어오면 각 요청을 메세지 큐에 저장하고 응답을 빠르게 비동기로 제공한다.

2) client의 응답은 빨리 도착하겠으나 실제 응답은 Processing이 될 것이다.

3) 그렇다면 고객은 후에 GET method를 한번 더 날려서 실제 데이터를 가져와야한다.

4) 이것은 메세지를 발송하거나 하는 컨셉에 대해서는 수용 가능하겠으나 단순 조회성 API에 적용하기에는 무리이다.

 

 

감사합니다.

'토이 프로젝트' 카테고리의 다른 글

웹서비스 성능 향상 방법  (0) 2024.10.30
API 서비스 개발(3) - API  (0) 2024.08.03
API 서비스 개발(2) - DB  (0) 2024.08.01
API 서비스 개발(1) - JWT  (0) 2024.07.29