RabbitMQ 성능 문제 해결: 느린 속도와 높은 CPU 사용량
RabbitMQ의 느린 속도와 높은 CPU 사용량을 진단하려면 큐, 소비자, 연결 변동, 디스크 I/O, 흐름 제어 및 클라이언트 동작을 확인하세요.
RabbitMQ 성능 문제 해결: 느린 속도와 높은 CPU 사용량
RabbitMQ는 강력하고 널리 사용되는 메시지 브로커이지만, 다른 분산 시스템과 마찬가지로 성능 저하가 발생할 수 있으며, 이는 일반적으로 느린 속도나 과도한 CPU 사용률로 나타납니다. 네트워크 구성, 디스크 I/O 또는 애플리케이션 로직 등 근본 원인을 식별하는 것은 시스템 상태와 낮은 지연 시간을 유지하는 데 중요합니다.
이 가이드는 RabbitMQ 배포에서 일반적인 성능 병목 현상을 진단하고 해결하기 위한 실용적인 문제 해결 매뉴얼 역할을 합니다. 중요한 모니터링 지점을 살펴보고 처리량을 최적화하고 CPU 부하를 안정화하여 메시지 브로커가 압박 속에서도 안정적으로 작동하도록 하는 실행 가능한 단계를 제공합니다.
초기 분류: 병목 현상 식별
깊은 구성 변경에 들어가기 전에 병목 현상이 어디서 발생하는지 정확히 파악하는 것이 중요합니다. 높은 CPU 또는 느린 속도는 일반적으로 네트워크 포화, 집중적인 디스크 I/O 또는 브로커와의 비효율적인 애플리케이션 상호 작용 중 하나를 가리킵니다.
1. RabbitMQ 상태 모니터링
첫 번째 단계는 RabbitMQ의 내장 모니터링 도구, 주로 관리 플러그인을 활용하는 것입니다.
주요 모니터링 지표:
- 메시지 속도: 시스템의 지속 가능한 용량을 초과하는 게시 또는 전달 속도의 급격한 증가를 찾으십시오.
- 큐 길이: 빠르게 증가하는 큐는 소비자가 생산자를 따라잡지 못하고 있음을 나타내며, 종종 메모리/디스크 압력 증가로 이어집니다.
- 채널/연결 활동: 높은 변동(연결/채널의 빈번한 열기 및 닫기)은 상당한 CPU 리소스를 소비합니다.
- 디스크 알람: 디스크 사용량이 구성된 임계값에 가까워지면 RabbitMQ는 데이터 손실을 방지하기 위해 의도적으로 메시지 전달 속도를 늦춥니다(흐름 제어).
2. 운영 체제 검사
RabbitMQ는 OS 수준의 리소스 경합에 민감한 Erlang VM에서 실행됩니다. 표준 도구를 사용하여 시스템 상태를 확인하십시오:
- CPU 사용량:
top또는htop을 사용하십시오.rabbitmq-server프로세스가 CPU의 대부분을 소비하고 있습니까? 그렇다면 Erlang 프로세스 분석을 조사하십시오(아래 섹션 참조). - I/O 대기:
iostat또는iotop을 사용하십시오. 높은 I/O 대기 시간은 특히 지속성이 많이 사용되는 경우 느린 디스크를 가리키는 경우가 많습니다. - 네트워크 지연 시간: 생산자, 소비자 및 브로커 노드 간에
ping을 사용하여 일반적인 네트워크 불안정성을 배제하십시오.
심층 분석: 높은 CPU 사용량 분석
RabbitMQ의 높은 CPU 사용량은 Erlang VM 또는 특정 프로토콜 활동에 의해 처리되는 집중적인 작업으로 인해 발생하는 경우가 많습니다.
Erlang 프로세스 부하 이해
Erlang 런타임은 프로세스를 효율적으로 관리하지만 특정 작업은 CPU 바운드입니다. RabbitMQ 서버 CPU 사용량이 모든 코어에서 100%로 고정되어 있으면 어떤 Erlang 프로세스 그룹이 책임이 있는지 조사하십시오.
프로토콜 핸들러(AMQP/MQTT/STOMP)
많은 클라이언트가 지속적으로 연결을 설정 및 해제하거나 대량의 작은 메시지를 게시하는 경우 인증, 채널 설정 및 패킷 처리의 CPU 비용이 크게 증가합니다. 빈번한 연결 변동은 주요 CPU 소모 요인입니다.
모범 사례: 지속적인 장기 연결을 선호하십시오. 클라이언트 측에서 연결 풀링을 사용하여 반복적인 핸드셰이크 및 설정 단계의 오버헤드를 최소화하십시오.
큐 인덱싱 및 지속성 메시지
큐가 많이 사용될 때, 특히 메시지가 지속성(디스크에 기록)인 경우 CPU 부하가 급증할 수 있습니다. 그 이유는 다음과 같습니다:
- 디스크 I/O 관리: 디스크 쓰기 및 버퍼 플러시 조정.
- 메시지 인덱싱: 특히 내구성이 높고 처리량이 많은 큐에서 큐 구조 내의 메시지 위치를 추적합니다.
제한 및 흐름 제어
RabbitMQ는 리소스가 제한될 때 자체를 보호하기 위해 흐름 제어를 구현합니다. 노드가 메모리 또는 디스크 공간에 대한 높은 워터마크에 도달하면 내부 제한을 적용하여 생산자에게 느린 속도로 나타날 수 있습니다.
흐름 제어로 인해 많은 메시지가 차단된 경우 즉각적인 해결책은 리소스를 확보하는 것입니다(예: 소비자가 활성화되어 있는지 확인하거나 디스크 공간을 늘리십시오). 장기적인 해결책은 클러스터를 확장하거나 소비자 처리량을 최적화하는 것입니다.
느린 소비자 및 큐 누적 문제 해결
소비자가 입력 속도를 따라잡지 못할 때 애플리케이션 계층에서 느린 속도가 인식되는 경우가 많습니다. 이는 일반적으로 소비자 측 문제 또는 소비자와 브로커 간의 네트워크 문제입니다.
소비자 확인 전략
소비자가 메시지를 확인하는 방식은 처리량과 브로커의 CPU 사용량에 큰 영향을 미칩니다.
- 수동 확인(
manual ack): 신뢰성을 제공하지만 소비자가 수신을 확인해야 합니다. 소비자가 중단되면 RabbitMQ는 메시지를 보유하여 잠재적으로 메모리를 백업하고 해당 큐의 다른 메시지에 지연을 유발합니다. - 자동 확인(
auto ack): 초기 처리량을 최대화하지만 소비자가 메시지를 수신한 후 처리하기 전에 충돌하면 메시지가 영원히 손실됩니다.
수동 확인을 사용하고 속도 저하가 발생하는 경우 관리 플러그인에서 확인되지 않은 메시지 수를 확인하십시오. 이 숫자가 높으면 소비자가 느리거나 확인에 실패하는 것입니다.
프리페치 수 최적화
qos(서비스 품질) 설정, 특히 프리페치 수는 소비자가 확인되지 않은 상태로 보유할 수 있는 메시지 수를 결정합니다.
프리페치 수가 너무 높게 설정되면(예: 1000) 단일 느린 소비자가 큐에서 많은 백로그를 가져와 동일한 큐에 있는 잠재적으로 더 빠른 다른 소비자를 굶주리게 할 수 있습니다.
예: 소비자가 초당 10개의 메시지만 처리하는 경우 prefetch_count를 100으로 설정하는 것은 낭비이며 불필요하게 부하를 집중시킵니다.
# 합리적인 프리페치 수 설정 예시 (예: 50)
# 클라이언트 라이브러리 등가물 사용 (개념적 표현)
channel.basic_qos(prefetch_count=50)
소비자와 브로커 간의 네트워크 지연 시간
소비자가 빠르지만 유선을 통해 수신된 메시지를 확인하는 데 오래 걸리는 경우 문제는 소비자와 연결된 RabbitMQ 노드 간의 지연 시간 또는 네트워크 포화일 가능성이 높습니다.
- 테스트: 일시적으로 소비자를 동일한 머신(로컬호스트)의 브로커에 연결하여 네트워크 변수를 제거하십시오. 성능이 크게 향상되면 네트워크 최적화(예: 전용 NIC, 중간 방화벽 확인)에 집중하십시오.
디스크 I/O 및 지속성 영향
디스크 성능은 특히 높은 내구성을 사용하는 큐의 경우 성능의 하드 한계인 경우가 많습니다.
지속성 메시지 및 내구성
- 내구성 있는 익스체인지 및 큐: 브로커 재시작 시 손실을 방지하는 데 필수적이지만 메타데이터 오버헤드가 발생합니다.
- 지속성 메시지: 지속성으로 표시된 메시지는 브로커가 생산자에게 확인을 보내기 전에 반드시 디스크에 기록되어야 합니다. 느린 디스크는 직접적으로 느린 생산자 처리량으로 이어집니다.
부하가 주로 일시적인(비지속성) 메시지로 구성된 경우 큐 자체가 내구성이 없도록 하거나, 더 실용적으로, 특정 페이로드에 대해 데이터 손실이 허용되는 경우 메시지를 일시적인 것으로 표시하십시오. 일시적인 메시지는 RAM에 남아 있기 때문에 훨씬 빠릅니다(메모리 압력에 따라 다름).
미러링 오버헤드
고가용성(HA) 클러스터에서 큐 미러링은 노드 간에 데이터를 복제합니다. 내결함성에 필수적이지만 미러링은 클러스터에 상당한 쓰기 부하를 추가합니다. 디스크 지연 시간이 높으면 이 부하가 I/O 용량을 포화시켜 모든 작업을 느리게 할 수 있습니다.
최적화 팁: 높은 쓰기 처리량이 필요하지만 장애 조치 중에 약간의 데이터 손실을 허용할 수 있는 큐(예: 로깅 스트림)의 경우 고가용성 노드 세트에서 미러링되지 않은 큐를 사용하거나 큐 길이가 매우 커질 것으로 예상되는 경우 지연 큐를 사용하는 것을 고려하십시오(지연 큐는 소비되지 않은 메시지를 더 빨리 디스크로 이동하여 RAM을 절약합니다).
브로커 문제와 애플리케이션 문제 분리
RabbitMQ는 종종 다른 곳에서 시작된 지연 시간에 대해 비난을 받습니다. 웹 요청이 시간 초과되고, 작업이 늦게 완료되거나, 다운스트림 데이터베이스가 느려지며, 큐의 깊이가 표시되기 때문에 큐가 가장 쉽게 눈에 띕니다. 브로커를 조정하기 전에 RabbitMQ가 느린지 또는 RabbitMQ가 소비자가 느리다는 것을 보여주는지 결정하십시오.
영향을 받는 큐에 대해 세 가지 숫자로 시작하십시오:
rabbitmqctl list_queues name messages_ready messages_unacknowledged consumers \
message_stats.publish_details.rate message_stats.deliver_get_details.rate \
message_stats.ack_details.rate
messages_ready가 소비자가 있는 동안 증가하고 확인이 느리면 소비자가 따라잡지 못하는 것입니다. 브로커는 정상일 수 있습니다. messages_unacknowledged가 증가하면 소비자가 메시지를 수신하고 있지만 완료하거나 확인하지 않는 것입니다. 디스크 또는 메모리 알람이 활성화된 동안 게시 확인이 느려지면 브로커가 백 프레셔를 적용하는 것입니다. CPU가 높고 연결 수가 증가하면 클라이언트 동작이 원인일 수 있습니다.
이 구분은 중요합니다. RabbitMQ에 RAM을 추가해도 모든 메시지에 대해 느린 API를 호출하는 데 2초를 소비하는 소비자를 고칠 수 없기 때문입니다. 소비자 복제본을 늘려도 디스크 쓰기에 의해 제한되는 브로커를 고칠 수 없습니다. 프리페치를 변경해도 각 게시에 대해 새 TCP 연결을 여는 생산자를 고칠 수 없습니다.
연결 및 채널 변동
RabbitMQ의 높은 CPU는 종종 지루합니다. 너무 많은 클라이언트가 반복적으로 연결을 열고 닫는 것입니다. AMQP 연결 설정은 무료가 아닙니다. 여기에는 TCP 설정, 선택적 TLS 협상, 인증, 튜닝 및 채널 협상이 포함됩니다. 애플리케이션이 모든 메시지, 모든 HTTP 요청 또는 모든 짧은 작업에 대해 연결을 열면 RabbitMQ는 메시지를 이동하는 대신 설정 작업에 CPU를 소비합니다.
연령 및 연결 수를 확인하십시오:
rabbitmqctl list_connections name user peer_host state channels connected_at
rabbitmqctl list_channels connection number user vhost
동일한 서비스에서 지속적으로 짧은 수명의 연결 스트림이 표시되면 클라이언트를 수정하십시오. 연결을 장기간 유지하십시오. 채널을 적절하게 사용하십시오. 대부분의 서비스는 시작 중에 연결을 만들고 종료될 때까지 재사용하며, 실패 시 재연결 로직을 포함해야 합니다. 웹 애플리케이션에서 프레임워크에 매우 신중한 연결 풀이 없는 한 요청 핸들러 내부에서 브로커 연결을 생성하지 마십시오.
TLS는 변동을 더 비싸게 만듭니다. TLS는 프로덕션에 적합하지만 반복적인 핸드셰이크는 부하가 걸릴 때 눈에 띄게 될 수 있습니다. 연결 재사용이 여전히 해결책입니다.
작업에 맞는 프리페치
프리페치는 마법의 처리량 노브가 아닙니다. 소비자가 보유할 수 있는 확인되지 않은 메시지 수를 제어합니다. 올바른 값은 처리 시간, 메시지 크기 및 소비자 간의 공정성에 따라 다릅니다.
프리페치가 1이면 간단하고 공정하지만 각 작업에 네트워크 또는 디스크에 대한 작은 대기 시간이 있을 때 소비자를 충분히 활용하지 못할 수 있습니다. 프리페치가 500이면 벤치마크에서 빠르게 보일 수 있지만 느린 소비자가 작업을 축적하고 충돌 시 재전송 문제를 증가시킬 수 있습니다.
실용적인 시작점은 소비자가 메시지당 소비하는 시간을 측정하는 것입니다. 작업이 CPU 집약적이고 각 프로세스가 한 번에 하나의 메시지를 처리하는 경우 프리페치를 낮게 유지하십시오. 작업이 원격 서비스를 기다리고 소비자가 내부적으로 동시성을 처리하는 경우 적당한 프리페치가 바쁘게 유지할 수 있습니다. 단계적으로 증가시키고 다음을 관찰하십시오:
- 확인 속도;
messages_unacknowledged;- 소비자 메모리;
- 종단 간 지연 시간;
- 소비자 재시작 후 재전송 횟수.
테스트에는 실패가 포함되어야 합니다. 소비자가 확인되지 않은 메시지를 보유하는 동안 하나를 종료하십시오. 재전송으로 인해 엄청난 양의 중복 작업이나 긴 중단이 발생하면 해당 큐에 대해 프리페치가 너무 높은 것입니다.
지속성 메시지 및 디스크 현실
지속성 메시지와 내구성 있는 큐는 중요한 작업에 올바른 선택이지만 병목 현상의 일부를 스토리지로 이동시킵니다. 게시자가 확인을 기다릴 때 느린 디스크 쓰기는 느린 게시로 나타납니다. 큐가 커지면 RabbitMQ는 더 많은 인덱스 및 스토리지 작업을 수행해야 합니다. 클러스터 설정에서 복제는 네트워크 및 디스크 작업도 추가합니다.
운영 체제에서 디스크 증상을 확인하십시오:
iostat -xz 1
vmstat 1
높은 I/O 대기, 높은 디스크 사용률 또는 긴 대기 시간은 브로커가 스토리지를 기다리고 있음을 알려줍니다. 이것이 "지속성을 끄십시오"를 의미하지는 않습니다. 더 빠른 스토리지, 불필요한 지속성 메시지 감소, 낮은 게시 속도, 더 효율적인 배치 또는 작업을 노드 간에 분산하는 토폴로지가 필요함을 의미합니다.
정확한 설정을 테스트하지 않는 한 RabbitMQ 데이터 디렉토리를 느린 네트워크 디스크에 두지 마십시오. RabbitMQ는 처리량만큼 지연 시간에 민감합니다. 대량 파일 복사에 허용 가능해 보이는 디스크도 메시지 워크로드에는 여전히 좋지 않을 수 있습니다.
큐 유형 및 복제 선택
이전 RabbitMQ 지침은 종종 미러링된 클래식 큐를 언급했습니다. 현재 RabbitMQ 배포에서는 쿼럼 큐가 복제된 내구성 워크로드에 일반적으로 선호되는 반면, 클래식 큐는 여전히 많은 비복제 또는 덜 중요한 경우에 적합합니다. 최선의 선택은 RabbitMQ 버전, 운영 요구 사항 및 워크로드에 따라 다릅니다.
쿼럼 큐는 복제된 내구성 큐의 장애 모델을 개선하지만 무료는 아닙니다. 합의 프로토콜을 통해 복제되므로 쓰기에는 여러 노드가 포함됩니다. 모든 고볼륨 일시적 이벤트 스트림을 쿼럼 큐에 넣으면 필요하지 않은 성능 문제가 발생할 수 있습니다.
비즈니스 가치에 맞는 더 강력한 내구성을 사용하십시오:
- 결제, 주문, 재고 및 감사 워크플로우는 종종 내구성 있는 복제된 큐가 필요합니다.
- 캐시 새로 고침, 메트릭 및 재구성 가능한 알림은 동일한 보호가 필요하지 않을 수 있습니다.
- 매우 큰 백로그는 더 큰 브로커만이 아닌 설계 검토가 필요할 수 있습니다.
요점은 안전을 최소화하는 것이 아닙니다. 재생성할 수 있는 데이터에 대해 가장 높은 안정성 비용을 지불하지 않으면서도 보호할 수 없는 메시지는 계속 보호하는 것입니다.
큰 메시지는 모든 것을 더 어렵게 만듭니다.
RabbitMQ는 큰 메시지를 전달할 수 있지만 메시지가 작을 때 큐는 일반적으로 더 건강합니다. 큰 이미지, 보고서, 아카이브 또는 전체 데이터베이스 내보내기를 포함하는 메시지는 메모리 압력, 디스크 압력, 네트워크 전송 시간 및 재전송 비용을 증가시킵니다.
큰 페이로드의 경우 페이로드를 객체 스토리지 또는 데이터베이스에 저장하고 참조를 포함하는 메시지를 보내십시오:
{
"job_id": "report-2026-05-25-001",
"object_url": "s3://reports-bucket/report-2026-05-25-001.json",
"sha256": "..."
}
소비자는 처리할 준비가 되면 페이로드를 가져옵니다. 이 설계는 완벽하지 않습니다. 이제 페이로드 저장소에 대한 수명 주기 정리 및 액세스 제어가 필요합니다. 그러나 RabbitMQ를 파일 전송 시스템으로 만드는 대신 조정에 집중하게 합니다.
CPU가 높지만 큐가 비어 있는 경우
빈 큐가 항상 RabbitMQ가 유휴 상태임을 의미하지는 않습니다. 클라이언트가 지속적으로 연결, 인증, 라우팅할 수 없는 메시지 게시, 토폴로지 선언 또는 비효율적인 패턴으로 폴링하기 때문에 CPU가 높을 수 있습니다.
관리 UI 또는 CLI에서 연결 변동 및 채널 수를 확인하십시오. 재연결 루프에 대한 애플리케이션 로그를 검토하십시오. 모든 게시 전에 익스체인지와 큐를 선언하는 클라이언트를 찾으십시오. 토폴로지 선언은 일반적으로 멱등적이지만 매우 높은 빈도로 수행하면 여전히 브로커 작업이 추가됩니다.
또한 플러그인을 확인하십시오. 관리, 페더레이션, 셔블, MQTT, STOMP, 추적 및 사용자 정의 플러그인은 활성화되고 사용될 때 모두 작업을 추가합니다. 인시던트 중에 플러그인을 맹목적으로 비활성화하지 말고 부하가 플러그인 활동과 일치하는지 확인하십시오.
더 안전한 튜닝 루틴
한 번에 하나씩 변경하고 전후 숫자를 기록하십시오. 프리페치, 소비자 수, 큐 유형, 지속성 및 하드웨어가 모두 동일한 배포에서 변경되면 RabbitMQ 성능 작업이 혼란스러워집니다.
유용한 루틴:
- 큐 속도, 큐 깊이, 확인되지 않은 메시지, 연결 수, CPU, 메모리, 디스크 I/O 및 게시 확인 지연 시간을 캡처합니다.
- 가능한 병목 현상을 선택합니다.
- 하나의 변경을 수행합니다.
- 동일한 워크로드를 실행합니다.
- 브로커 처리량뿐만 아니라 종단 간 지연 시간을 비교합니다.
인시던트가 활성화된 경우 먼저 되돌릴 수 있는 변경을 선택하십시오: 소비자 추가, 연결 변동 중지, 생산자 속도 감소, 백로그 소진 또는 선택적 워크로드 이동. 시스템이 이미 다운되었거나 더 안전한 경로가 없는 경우가 아니라면 큐 유형 마이그레이션 및 스토리지 재설계는 계획된 작업으로 남겨두십시오.
실행 가능한 단계 요약
높은 CPU 또는 일반적인 느린 속도에 직면했을 때 다음 체크리스트를 따르십시오:
- 알람 확인: 디스크 또는 메모리 흐름 제어 알람이 활성화되어 있지 않은지 확인합니다.
- 클라이언트 동작 검사: 높은 연결/채널 변동 또는 부적절하게
auto-ack을 사용하는 클라이언트를 찾습니다. - 소비자 최적화:
prefetch_count를 소비자의 실제 처리 속도에 맞게 조정합니다. - 디스크 속도 확인: 스토리지 백엔드가 지속성 및 복제 요구 사항에 충분히 빠른지 확인합니다.
- Erlang 프로파일링(고급): Erlang 도구(예:
observer)를 사용하여 CPU가 프로토콜 처리에 사용되는지 내부 큐 관리에 사용되는지 확인합니다.
OS, 브로커 및 애플리케이션 계층에서 리소스 사용률을 체계적으로 분석하면 RabbitMQ 성능 문제의 근본 원인을 효과적으로 격리하고 제거할 수 있습니다.