RabbitMQ 메모리 관리 및 높은 처리량을 위한 모범 사례
높은 처리량이 브로커에 압박을 가하지 않도록 RabbitMQ 메모리, 디스크 한도, 큐 및 소비자를 조정합니다.
RabbitMQ 메모리 관리 및 높은 처리량을 위한 모범 사례
RabbitMQ는 많은 메시지를 처리할 수 있지만, 메모리가 오버플로우 계획이 될 때는 좋지 않습니다. 브로커는 연결, 채널, 큐 프로세스, 메시지 메타데이터, 확인되지 않은 전송, 플러그인, 메트릭 및 Erlang 런타임 자체를 위해 메모리가 필요합니다. 게시자가 소비자보다 오랫동안 빠르면 문제는 "RabbitMQ가 얼마나 빠를 수 있습니까?"에서 "압력이 먼저 어디에 나타날까?"로 바뀝니다.
좋은 메모리 관리는 주로 그 압력을 가시적이고 통제된 상태로 유지하는 것입니다. 운영 체제가 프로세스를 종료하기 전에 RabbitMQ가 백프레셔를 적용하기를 원합니다. 또한 영구 메시지와 내부 상태를 안전하게 쓸 수 있을 만큼 충분한 디스크 여유 공간이 필요합니다.
메모리 알람으로 시작하지만, 튜닝 마법처럼 취급하지 마십시오
RabbitMQ는 vm_memory_high_watermark를 사용하여 메모리 사용량이 너무 높은 시점을 결정합니다. 임계값을 초과하면 RabbitMQ는 메모리 알람을 발생시키고 메모리가 낮아질 때까지 게시자를 차단합니다. 그 동작은 의도적입니다. 차단된 게시자는 성가시지만, 메모리 부족 브로커는 더 나쁩니다.
일반적인 시작점은 사용 가능한 메모리의 약 40%인 상대적 워터마크입니다:
vm_memory_high_watermark.relative = 0.40
그 숫자는 절대적이지 않습니다. 다른 서비스가 있는 작은 VM은 더 낮은 임계값이 필요할 수 있습니다. 워크로드를 잘 이해하는 전용 브로커는 다른 값을 허용할 수 있습니다. 요점은 OS 페이지 캐시, 파일 시스템 활동, 모니터링 에이전트 및 그래프가 따라잡기 전에 발생하는 버스트를 위한 공간을 남겨두는 것입니다.
절대값을 설정할 수도 있으며, 이는 컨테이너나 "사용 가능한 메모리"가 오해될 수 있는 환경에서 종종 더 쉽습니다:
vm_memory_high_watermark.absolute = 6GiB
노드가 배포된 방식과 일치하는 한 가지 스타일을 사용하십시오. 컨테이너에서는 RabbitMQ가 호스트의 전체 메모리가 아닌 예상하는 컨테이너 제한을 보는지 확인하십시오. 잘못된 메모리 총계를 기반으로 한 워터마크는 조용히 프로덕션 인시던트를 만드는 방법입니다.
디스크 여유 한도는 다른 안전 레일입니다
RabbitMQ의 디스크 보호 설정은 disk_free_limit입니다. 이는 disk_high_watermark 백분율이 아닌 여유 공간을 기반으로 합니다. 사용 가능한 디스크 공간이 구성된 한도 아래로 떨어지면 RabbitMQ는 디스크 알람을 발생시키고 게시자를 차단합니다.
많은 프로덕션 노드의 경우 절대 한도가 상대적 한도보다 더 명확합니다:
disk_free_limit.absolute = 20GB
올바른 값은 메시지 크기, 게시 속도, 지속성, 로그 순환 및 팀이 공간을 추가하거나 큐를 비울 수 있는 속도에 따라 다릅니다. 큰 영구 메시지를 수신하는 노드는 작은 일시적 이벤트를 처리하는 노드보다 훨씬 더 큰 완충 장치가 필요합니다.
알람을 피하기 위해 이 값을 작게 설정하지 마십시오. 디스크 알람은 브로커를 보호하기 위해 있습니다. 디스크가 0바이트에 도달하면 쓰기 실패, 가용성 손상 및 훨씬 더 지저분한 복구가 발생할 수 있습니다.
실제로 메모리를 사용하는 것이 무엇인지 이해하십시오
메모리가 상승할 때 추측하지 마십시오. RabbitMQ는 관리 UI와 CLI를 통해 메모리 분석을 제공합니다:
rabbitmq-diagnostics memory_breakdown
rabbitmqctl status
rabbitmqctl list_queues name type messages_ready messages_unacknowledged memory
가장 유용한 첫 번째 분할은 준비된 메시지와 확인되지 않은 메시지입니다. 준비된 메시지는 여전히 큐에서 대기 중입니다. 확인되지 않은 메시지는 소비자에게 전달되었으며 basic.ack, basic.nack 또는 채널 종료를 기다리고 있습니다.
준비된 메시지가 증가하고 있다면 생산자가 소비자를 앞지르고 있거나 소비자가 연결되지 않은 것입니다. 확인되지 않은 메시지가 증가하고 있다면 소비자가 메시지를 받고 있지만 완료하지 않고 있는 것입니다. 이들은 다른 문제입니다. 메모리 한도를 높이는 것은 흐름 불균형이 남아 있는 한 시간만 벌 뿐입니다.
큰 메시지는 특별한 주의가 필요합니다. 적당한 메시지 수를 가진 큐도 각 메시지가 큰 페이로드를 전달하면 여전히 많은 메모리를 소비할 수 있습니다. 메시지에 이미지, 문서 또는 큰 JSON 블롭이 포함된 경우 페이로드를 다른 곳에 저장하고 RabbitMQ를 통해 참조를 보내는 것을 고려하십시오. 메시지 브로커는 일반적으로 블롭 저장소 역할보다는 작업 알림을 이동하는 데 더 적합합니다.
프리페치를 조정하여 숨겨진 백로그를 중지하십시오
프리페치는 RabbitMQ가 소비자에게 전달할 수 있는 확인되지 않은 메시지 수를 제어합니다. 높은 프리페치 값은 빠른 소비자의 처리량을 향상시킬 수 있지만, 백로그를 큐에서 소비자 메모리로 이동시킵니다.
예를 들어, prefetch_count=500인 10명의 소비자는 준비된 큐 외부에 최대 5,000개의 확인되지 않은 메시지를 보유할 수 있습니다. 각 메시지가 크거나 처리 속도가 느리면 메모리 압력과 불균일한 지연 시간이 발생할 수 있습니다. 새 메시지는 이미 느린 소비자 내부에 있는 수백 개의 이전 메시지 뒤에서 기다려야 할 수 있습니다.
작업과 일치하는 프리페치 값으로 시작하십시오. 느린 API 호출이나 데이터베이스 쓰기의 경우 5 또는 10과 같은 작은 숫자를 시도하고 측정한 후에만 증가시키십시오. 매우 빠른 로컬 CPU 작업의 경우 더 높은 값이 도움이 될 수 있습니다. 엄격한 공정성을 위해 prefetch_count=1이 때로는 올바른 절충안이며, 전체 처리량이 낮더라도 마찬가지입니다.
핵심은 처리 시간과 ack 지연을 측정하는 것입니다. RabbitMQ는 사용자를 위해 메시지를 완료할 수 없습니다. 완료되지 않은 작업을 얼마나 많이 나눠줄지만 제한할 수 있습니다.
가능하면 큐를 짧게 유지하십시오
RabbitMQ는 메시지가 큐에 몇 시간 동안 머무르지 않고 시스템을 통해 흐를 때 가장 잘 수행됩니다. 일반적으로 0에 가깝고 가끔 스파이크가 발생하는 큐는 건강합니다. 하루 종일 증가하고 밤에 비워지는 큐는 용량 경고입니다. 증가만 하는 큐는 느린 속도의 중단입니다.
긴 백로그의 경우 백로그가 예상되는지 결정하십시오. 예상된다면 맞는 큐 유형과 스토리지 설계를 사용하십시오. 쿼럼 큐는 내구성 있는 복제 워크로드에 좋습니다. 스트림은 재생 스타일 워크로드에 적합할 수 있습니다. 클래식 큐는 더 간단한 일시적 작업에 적합할 수 있습니다. 백로그가 예상되지 않는다면 브로커 메모리를 조정하기 전에 소비자나 다운스트림 서비스를 수정하십시오.
만료된 작업이 진정으로 쓸모없을 때만 메시지 TTL을 설정하십시오. TTL은 용량을 대체하지 않습니다. 시스템이 오래된 메시지를 처리하지 못하게 보호할 수 있지만, 무심코 적용하면 데이터 손실을 숨길 수 있습니다.
데드 레터 큐는 독 메시지를 정상 흐름과 분리하는 데 도움이 됩니다. 데드 레터 전략 없이 하나의 잘못된 페이로드가 영원히 재시도되어 리소스를 소비하고 큐가 실제보다 느리게 보이게 만들 수 있습니다.
지속성은 처리량 예산을 변경합니다
내구성 있는 큐와 영구 메시지는 메시지가 브로커 재시작 후에도 살아남아야 할 때 올바른 선택입니다. 또한 디스크 쓰기가 필요합니다. 게시자 확인은 브로커가 메시지에 대한 책임을 수락했음을 게시자에게 알리는 신뢰성 신호를 추가합니다.
느린 패턴은 하나의 영구 메시지를 게시하고, 동기적으로 확인을 기다린 다음, 다음을 게시하는 것입니다. 간단하고 안전하지만 처리량은 왕복 시간과 디스크 동작에 의해 제한됩니다. 더 나은 패턴은 비동기 게시자 확인이나 작은 배치를 사용하면서 부정적인 확인과 타임아웃을 처리하는 것입니다.
매우 특별한 이유가 없다면 높은 처리량 게시를 위해 AMQP 트랜잭션을 피하십시오. 게시자 확인은 RabbitMQ 게시자를 위한 일반적인 신뢰성 도구입니다.
RabbitMQ에 지루한 인프라를 제공하십시오
RabbitMQ는 예측 가능한 기계를 좋아합니다: 충분한 메모리, 영구 워크로드를 위한 빠른 디스크, 안정적인 네트워크 지연 시간, CPU를 훔치는 시끄러운 이웃이 없어야 합니다. 브로커가 데이터베이스, 로그 프로세서 및 무작위 cron 작업과 호스트를 공유하면 메모리 튜닝은 추측이 됩니다.
영구적인 높은 처리량 큐에는 SSD 또는 NVMe 스토리지를 사용하십시오. 디스크 사용률뿐만 아니라 디스크 지연 시간을 관찰하십시오. 디스크는 적당한 처리량을 보이면서도 여전히 고통스러운 쓰기 지연 시간을 가질 수 있습니다. 클라우드 환경에서는 프로비저닝된 IOPS와 버스트 크레딧이 디스크 레이블보다 더 중요할 수 있습니다.
연결 변동을 제한하십시오. 오래 지속되는 연결과 채널은 모든 게시에 대해 새 연결을 여는 것보다 저렴합니다. 애플리케이션이 수천 개의 단기 연결을 생성하면 메시지 속도가 평범하더라도 메모리와 파일 디스크립터 사용량이 증가할 수 있습니다.
컨테이너는 명시적인 사고가 필요합니다
RabbitMQ는 컨테이너에서 잘 실행되지만 메모리 제한은 명확해야 합니다. 브로커의 메모리 워터마크는 컨테이너가 실제로 사용할 수 있는 제한에 대해 계산될 때만 유용합니다. RabbitMQ가 호스트의 메모리를 가지고 있다고 생각하지만 컨테이너 런타임이 더 작은 제한을 적용하면 RabbitMQ의 자체 알람 동작이 보호하기 전에 컨테이너가 종료될 수 있습니다.
컨테이너 메모리 제한을 설정한 다음 그 제한 내에서 공간을 남겨두는 절대 RabbitMQ 워터마크를 설정하십시오:
vm_memory_high_watermark.absolute = 3GiB
예를 들어, 4 GiB로 제한된 컨테이너에서 3 GiB 브로커 워터마크는 전용 팟에 대해 합리적일 수 있지만, 사이드카나 플러그인이 의미 있는 메모리를 사용하는 경우 더 낮은 값이 더 나을 수 있습니다. 그 숫자를 맹목적으로 복사하지 마십시오. 요점은 관계를 명시적으로 만드는 것입니다.
영구 데이터에는 영구 스토리지도 필요합니다. 컨테이너 재시작이 RabbitMQ 데이터 디렉토리를 잃으면 내구성 있는 큐와 영구 메시지가 당신을 구하지 못할 것입니다. 적절한 볼륨을 사용하고, 스토리지 클래스를 이해하며, 설정을 신뢰하기 전에 브로커 재시작을 테스트하십시오.
지연 큐, 쿼럼 큐 및 메모리 기대치
오래된 RabbitMQ 조언은 종종 "큰 백로그를 위해 지연 큐를 사용하십시오"라고 말합니다. 그 조언에는 맥락이 필요합니다. 클래식 지연 큐는 더 많은 메시지를 디스크에 유지하고 긴 큐의 메모리 압력을 줄이기 위해 설계되었습니다. 큰 백로그가 예상되는 클래식 큐 워크로드에 여전히 유용할 수 있습니다.
쿼럼 큐는 다르게 동작하며 일반적으로 복제된 내구성 워크로드에 사용됩니다. 백로그를 처리할 수 있지만 데이터를 복제하고 자체 메모리 및 디스크 프로필이 있습니다. 쿼럼 큐는 먼저 신뢰성 선택입니다. 무제한 백로그를 위한 지름길이 아닙니다.
비즈니스가 메시지가 며칠 동안 앉아 있고 많은 소비자에 의해 재생되기를 기대한다면 스트림이나 다른 로그 스타일 시스템이 일반 작업 큐보다 더 적합할 수 있습니다. RabbitMQ는 작업을 발송하는 데 탁월합니다. 큰 역사적 페이로드를 위한 유일한 장기 저장소 계층이 될 때는 덜 즐겁습니다.
브로커 증상을 워크로드 증상과 분리하십시오
메모리 알람은 RabbitMQ가 압박을 받고 있다는 것을 알려줍니다. RabbitMQ가 근본 원인인지 알려주지 않습니다. 느린 청구 API는 소비자가 ack를 중단하게 하고, 이는 확인되지 않은 메시지가 증가하게 하며, 브로커 메모리를 높이고, 게시자를 차단합니다. 브로커 알람은 실제이지만 첫 번째 수정은 브로커 외부에 있을 수 있습니다.
검토 중에 게시 속도, 전달 속도, ack 속도, 준비된 메시지, 확인되지 않은 메시지, 메모리, 디스크 여유 공간 및 소비자 처리 시간을 함께 그래프로 표시하십시오. 움직임의 순서가 중요합니다. 메모리가 상승하기 전에 ack 속도가 떨어지면 소비자를 살펴보십시오. 확인이 느려지기 전에 디스크 지연 시간이 스파이크되면 스토리지를 살펴보십시오. 제품 출시 후 게시 속도가 두 배가 되면 용량과 백프레셔를 살펴보십시오.
이것이 부하 테스트에 소비자와 다운스트림 종속성을 포함해야 하는 이유이기도 합니다. 게시 전용 벤치마크는 실제 워크플로에 대해 거의 증명하지 않습니다. 브로커는 한동안 메시지를 빠르게 수락할 수 있지만, 시스템은 소비자가 필요한 속도로 메시지를 완료할 때만 작동합니다.
백프레셔를 애플리케이션 팀에 가시적으로 만드십시오
게시자 차단은 보이지 않아서는 안 됩니다. 애플리케이션은 클라이언트 라이브러리가 노출할 때 차단 및 차단 해제된 연결 이벤트를 기록해야 하며, 게시자는 사용자 요청을 처리하는 게시 경로 주변에 타임아웃이 있어야 합니다.
그 가시성 없이 메모리 알람은 모호한 "앱이 느리다"는 불평이 됩니다. 그것으로 팀은 RabbitMQ가 특정 시간에 백프레셔를 적용했음을 볼 수 있으며, 그 타임스탬프를 큐 깊이, 소비자 오류, 디스크 지연 시간 및 배포 이벤트와 비교할 수 있습니다.
높은 처리량 검토 중 확인하는 사항
다음 질문으로 시작합니다:
- 메모리 알람이나 디스크 알람이 발생하고 있습니까?
- 메시지가 주로 준비되었습니까 아니면 확인되지 않았습니까?
- 어떤 큐가 가장 많은 메모리를 사용합니까?
- 소비자가 게시 속도를 따라잡고 있습니까?
- 게시자 확인이 비동기식입니까 아니면 하나씩 차단합니까?
- 메시지가 필요한 것보다 큽니까?
- 버스트 중에 디스크 지연 시간이 증가하고 있습니까?
- 연결이 안정적입니까, 아니면 계속 재연결되고 있습니까?
그 답변은 일반적으로 수정을 가리킵니다. 때로는 수정이 구성 변경입니다. 더 자주는 흐름 변경입니다: 더 빠른 소비자, 더 낮은 프리페치, 더 작은 메시지, 더 나은 배치, 데드 레터 경로 또는 워크로드와 일치하는 큐 유형.
높은 처리량은 단지 벤치마크에서 더 큰 숫자가 아닙니다. 메모리, 디스크 및 지연 시간에 대한 통제를 잃지 않고 바쁜 기간을 흡수하는 능력입니다. RabbitMQ는 안전 레일을 제공하지만 여전히 트래픽을 계속 움직이게 해야 합니다.