RabbitMQ 성능 향상을 위한 가이드
Queues
큐에 쌓여있는 메시지는 적어야한다
- 큐에 있는 많은 메시지는 RAM 사용에 많은 부하를 줄 수 있습니다.
- RAM을 확보하기 위해 RabbitMQ는 메시지를 디스크로 플러시(페이지 아웃)하기 시작합니다.
- 페이지 아웃 프로세스는 일반적으로 시간이 걸리고 페이지 아웃할 메시지가 많을 때 큐가 메시지를 처리하지 못하도록 차단하여 큐잉 속도를 저하시킵니다.
- 큐에 메시지가 많으면 브로커의 성능에 부정적인 영향을 미칠 수 있습니다.
예측 가능한 성능을 얻기 위해서 Lazy Queue를 활성화하라
- Lazy queue는 메시지가 자동으로 디스크에 저장되어 RAM 사용을 최소화하지만 처리 시간을 연장하는 큐입니다.
- 한 번에 많은 메시지를 보내거나(예: 배치 작업 처리),
- 소비자가 항상 게시자의 속도를 따라잡지 못할 것이라고 생각되는 경우 Lazy Queue를 활성화하는 것이 좋습니다.
- 매우 높은 성능이 필요하거나 큐가 항상 짧거나 최대 길이 정책을 설정한 경우 지연 큐을 비활성화해야 합니다.
TTL 또는 최대 길이로 큐 크기 제한
- 많은 양의 메시지 처리를 위해선 큐에 최대 길이를 설정을 권장합니다.
- 최대 길이 설정보다 커지지 않도록 큐 헤드에서 메시지를 삭제하여 큐을 짧게 유지해야합니다.
큐의 개수
- 큐는 RabbitMQ에서 단일 스레드입니다.
- 큐 1개의 성능은 하나의 CPU 코어로 제한됩니다.
- RabbitMQ 클러스터가 있는 경우 큐을 다른 코어와 다른 노드로 분할하면 더 나은 성능을 얻을 수 있습니다.
- 여러개의 큐와 소비자가 있는 경우, CPU 코어 개수만큼 많은 큐가 있는 경우, 멀티 코어 시스템에서 더 나은 처리량을 얻을 수 있습니다.
- 단, 너무 많은 큐와 소비자가 있는 경우는 속도가 느려질 수 있습니다.
- 또한, RabbitMQ에서 처리를 중단시키고 CPU 사용량을 줄입니다.
미사용 큐 자동삭제
- 클라이언트 연결이 실패하고 사용되지 않는 리소스(큐)가 있으면 성능에 영향을 줄 수 있습니다.
- 큐를 자동 삭제하는 방법이 3가지 있습니다.
- 1.큐에서 TTL 정책을 설정합니다.
- ex> 28일의 TTL 정책은 28일 동안 사용되지 않은 큐을 삭제합니다.
- 2.자동 삭제 큐은 마지막 소비자가 취소되거나 채널/연결이 닫히면(또는 서버와의 TCP 연결이 끊어지면) 삭제됩니다.
- 3.Exclusive 큐는 Connection이 선언된 경우만 사용됩니다. Connection이 끊기면 큐도 삭제됩니다.
Payload - RabbitMQ message size and types
- 초당 처리되는 메시지의 양을 결정 짓는 건, 메시지의 크기와 수량입니다.
- 크기가 큰 메시지를 적게 처리할 때보다, 작은 메시지를 많이 처리할 때 메시지 처리에 영향을 더 미칩니다.
- 이를 해결하려면 작은 메시지를 하나의 더 큰 메시지로 묶고 소비자가 그것을 나누도록 하는 것입니다.
- 그러나 여러 메시지를 묶는 경우 처리 시간에 영향을 미칠 수 있습니다.
Connection and Channel
- 각 Connection은 약 100KB의 RAM을 사용합니다(TLS를 사용하는 경우 더 많음).
- 최악의 경우 메모리 부족으로 인해 서버가 중단될 수 있습니다.
- 가능한한 Connection을 재사용하고 채널이 있는 스레드 간에 Connection을 다중화하는 것입니다.
- 이상적으로는 프로세스당 하나의 연결이 있어야 하며 애플리케이션에서 스레드당 하나의 채널을 사용해야 합니다.
스레드 간에 채널을 공유하면 안된다
- 대부분의 클라이언트는 성능상의 이유로 채널을 스레드로부터 안전하게 만들지 않으므로 스레드 간에 채널을 공유하지 마십시오.
연결 또는 채널을 반복적으로 열고 닫으면 안된다
- 연결과 채널을 반복적으로 열고 닫지 않도록 하십시오.
- 이렇게 하면 더 많은 TCP 패키지를 보내고 받아야 하므로 대기 시간이 길어집니다.
게시자(publisher)와 소비자(consumer)는 각각의 Connection을 사용하라
- 게시자와 소비자에 대한 연결을 분리하여 높은 처리량을 달성합니다.
- 게시자가 서버가 처리하기에 너무 많은 메시지를 보내는 경우 RabbitMQ는 TCP 연결에 역 압력을 가할 수 있습니다.
- 동일한 TCP 연결에서 소비하는 경우 서버는 클라이언트로부터 메시지 승인을 받지 못하여 소비 성능에 영향을 미칠 수 있습니다.
- 소비 속도가 낮으면 서버에 과부하가 걸립니다.
Acknowledgements and Confirms
- 클라이언트는 메시지를 받았을 때 또는 클라이언트가 메시지를 완전히 처리했을 때 메시지를 ack할 수 있습니다.
- Acknowledgements기능은 성능에 영향을 미치므로 빠른 처리를 위해선 비활성화를 해야합니다.
Unacknowledged messages
- 확인되지 않은 모든 메시지는 서버의 RAM에 있어야 합니다.
- 확인되지 않은 메시지가 너무 많으면 메모리가 부족해집니다.
- 확인되지 않은 메시지를 제한하는 효율적인 방법은 클라이언트가 미리 가져오는 메시지 수를 제한하는 것입니다.
- 해당 내용은 prefetch 섹션에서 참고해야합니다.
Persistent messages and durable queue
- 메시지를 잃을 수 없다면, 큐를 "durable"로 선언하고, 전달 모드를 "persistent"로 설정하여 메시지를 전송해야 합니다.
- 이렇게 하면 메시지와 브로커 정의가 재시작을 견딜 수 있도록 디스크에 저장되므로 브로커 재시작, 하드웨어 오류 또는 충돌과 같은 상황에서 메시지 유실을 방지할 수 있습니다.
- 그러나 persistent 메시지는 디스크에 쓰여야 하므로 성능이 저하될 수 있습니다.
- 또한, lazy queue는 transient 메시지를 보내더라도 동일한 성능 저하를 유발할 수 있습니다.
- 따라서 고성능을 위해서는 transient 메시지를 사용하는 것이 좋습니다.
Prefetch
- prefetch의 Default 값은 무제한입니다.
- prefetch 값은 소비자에게 한 번에 전송되는 메시지의 수를 지정합니다.
- prefetch 값이 높은 경우
- 클라이언트는 더 많은 메시지를 미리 받아 처리할 수 있습니다.
- ex> 소비자는 더 많은 메시지를 미리 받을 수 있으므로 더 많은 작업을 처리할 수 있습니다.
- 하지만 클라이언트가 처리할 수 있는 메시지 양보다 더 많은 메시지를 받는다면,
- 클라이언트는 처리할 수 없는 메시지를 다시 큐에 반환해야 합니다.
- 따라서 prefetch 값을 너무 높게 설정하면,
- 일부 소비자는 불필요하게 메시지를 받게 되고,
- 일부 소비자는 메시지를 받지 못할 수도 있습니다.
- prefetch 값이 낮은 경우
- 소비자는 메시지 도착을 기다리는데 더 많은 시간을 소비합니다.
- ex> 소비자는 적은 메시지를 받고 처리하면, 나머지 시간은 대기 상태가 됩니다.
- 그러나 메시지 수신 속도가 일정하게 유지된다면,
- 소비자가 처리할 수 있는 평균 메시지 처리 시간으로 prefetch 값을 설정하면 됩니다.
Routing (exchange setup)
- Exchange의 메시지 처리 방식 4가지 중 Direct exchange 방법이 처리 속도가 빠릅니다.
- Binding이 많다는 건 어디로 보내야할지 계산하는 시간이 오래 걸릴 수 있습니다.