컨슈머
컨슈머 (Consumers)
컨슈머는 큐에서 메시지를 받아 처리하고 인정하는 애플리케이션이에요. 이번엔 컨슈머 등록·생명주기, 인정 모드, prefetch, 단일 활성 컨슈머, 연결 복구, 동시성까지 컨슈머 전반을 다뤄볼게요.
본문
이 가이드는 컨슈머와 관련된 다양한 주제를 다뤄요.
- 기초
- 컨슈머 생명주기
- 컨슈머 등록 방법(구독, "push API")
- 인정 모드
- 메시지 속성과 전달 메타데이터
- prefetch로 미해결 전달 수 제한
- 전달 인정 타임아웃
- 컨슈머 용량 지표
- 컨슈머 취소 방법
- 컨슈머 독점성
- 단일 활성 컨슈머
- 컨슈머 활동
- 컨슈머 우선순위
- 연결 실패 복구
- 예외 처리
- 동시성 고려사항
등등이요.
컨슈머란
"컨슈머"라는 용어는 맥락에 따라 다른 의미를 가져요. 메시징·스트리밍 맥락에서 일반적으로 컨슈머는 메시지를 소비하고 인정하는 애플리케이션(또는 애플리케이션 인스턴스)이에요. 같은 애플리케이션이 동시에 메시지를 게시해 퍼블리셔일 수도 있어요.
메시징 프로토콜에는 메시지 전달을 위한 지속적 구독이라는 개념도 있어요. 구독(subscription)은 그런 엔티티를 부르는 한 용어이고, 컨슈머(consumer)도 또 다른 용어예요. RabbitMQ가 지원하는 프로토콜은 두 용어를 모두 쓰지만, RabbitMQ 문서는 후자를 선호해요. 이런 의미에서 컨슈머는 전달이 시작되기 전에 등록되어야 하고 애플리케이션이 취소할 수 있는 메시지 전달 구독이에요.
RabbitMQ는 메시징 브로커예요. 퍼블리셔로부터 메시지를 받아 라우팅하고, 라우팅할 큐가 있으면 소비되도록 저장하거나, 컨슈머가 있으면 즉시 전달해요. 컨슈머는 큐에서 소비해요. 메시지를 소비하려면 큐가 있어야 해요. 새 컨슈머가 추가되면, 큐에 이미 준비된 메시지가 있다면 전달이 즉시 시작돼요. 컨슈머 등록 시점에 대상 큐가 비어 있을 수도 있는데, 그런 경우 첫 전달은 새 메시지가 인큐될 때 일어나요. 존재하지 않는 큐에서 소비하려 하면 404 Not Found 코드의 채널 레벨 예외가 발생하고, 그 시도가 일어난 채널이 닫혀요.
모든 컨슈머에는 클라이언트 라이브러리가 주어진 전달에 대해 어떤 핸들러를 호출할지 정하는 데 쓰는 식별자가 있어요. 이름은 프로토콜마다 달라요. 컨슈머 태그(consumer tag)와 구독 ID(subscription ID)가 가장 흔히 쓰이는 두 용어예요. RabbitMQ 문서는 전자를 선호해요. 컨슈머 태그는 컨슈머 취소에도 쓰여요.
컨슈머는 오래 살도록 만들어져요. 컨슈머의 수명 동안 여러 전달을 받는 식이죠. 컨슈머를 등록해서 단일 메시지만 소비하는 건 최적이 아니에요. 컨슈머는 보통 애플리케이션 시작 시 등록돼요. 연결이나 애플리케이션이 살아 있는 한 존재하는 경우가 많아요. 컨슈머는 더 동적일 수도 있고, 시스템 이벤트에 반응해 등록하고 더 이상 필요 없으면 구독을 해제할 수도 있어요. Web STOMP·Web MQTT 플러그인으로 쓰는 WebSocket 클라이언트·모바일 클라이언트 등에서 흔해요.
연결 실패와 복구
클라이언트가 RabbitMQ에 대한 연결을 잃을 수 있어요. 연결 손실이 감지되면 메시지 전달이 멈춰요. 일부 클라이언트 라이브러리는 컨슈머 복구를 포함한 자동 연결 복구 기능을 제공해요. Java·.NET·Bunny가 그런 라이브러리예요. 연결 복구가 100%의 시나리오·워크로드를 덮을 수는 없지만, 일반적으로 소비 애플리케이션에서 매우 잘 작동하고 권장돼요.
다른 클라이언트 라이브러리에서는 애플리케이션 개발자가 연결 복구를 직접 수행해야 해요. 보통 다음 복구 순서가 잘 작동해요.
- 연결 복구
- 채널 복구
- 큐 복구
- 익스체인지 복구
- 바인딩 복구
- 컨슈머 복구
다시 말해, 컨슈머는 보통 대상 큐와 그 큐의 바인딩이 준비된 뒤에 마지막으로 복구돼요.
important: 자동 삭제·독점 큐를 쓰는 연결을 자동 복구할 때는 그 큐들이 서버 명명이 되도록 해야 해요.
푸시 API와 전달 메타데이터
애플리케이션은 RabbitMQ가 인큐된 메시지(전달)를 푸시하게 구독할 수 있어요. 이는 큐에 컨슈머(구독)를 등록해서 해요. 구독이 자리 잡으면 RabbitMQ가 메시지 전달을 시작하고, 각 전달에 대해 사용자 제공 핸들러가 호출돼요. 클라이언트 라이브러리에 따라 특정 인터페이스를 따르는 사용자 제공 함수 또는 객체일 수 있어요. 성공한 구독 연산은 구독 식별자(컨슈머 태그)를 반환하고, 나중에 컨슈머를 취소하는 데 쓸 수 있어요.
각 전달은 메시지 메타데이터와 전달 정보를 결합해요. 클라이언트 라이브러리마다 이런 속성에 접근하는 방식이 조금씩 달라요. 일반적으로 전달 핸들러는 전달 데이터 구조에 접근할 수 있어요. 다음 속성은 전달·라우팅 세부 정보로, 그 자체로 메시지 속성이 아니며 RabbitMQ가 라우팅·전달 시점에 설정해요.
| 속성 | 타입 | 설명 |
|---|---|---|
| Delivery tag | 양의 정수 | 전달 식별자, Confirms 참고. |
| Redelivered | Boolean | 이 메시지가 이전에 전달·재큐잉되었으면 true |
| Exchange | String | 이 메시지를 라우팅한 익스체인지 |
| Routing key | String | 퍼블리셔가 사용한 라우팅 키 |
| Consumer tag | String | 컨슈머(구독) 식별자 |
다음은 메시지 속성이에요. 대부분 선택적이고 퍼블리셔가 게시 시점에 설정해요.
| 속성 | 타입 | 설명 | 필수? |
|---|---|---|---|
| Delivery mode | Enum (1 또는 2) | 2는 "persistent", 1은 "transient". 일부 클라이언트 라이브러리는 이 속성을 불리언·enum으로 노출 | 예 |
| Type | String | 애플리케이션 특정 메시지 타입, 예: "orders.created" | 아니요 |
| Headers | Map (string => any) | 문자열 헤더 이름의 임의 헤더 맵 | 아니요 |
| Content type | String | 콘텐츠 타입, 예: "application/json". RabbitMQ 코어가 아니라 애플리케이션이 사용 | 아니요 |
| Content encoding | String | 콘텐츠 인코딩, 예: "gzip". RabbitMQ 코어가 아니라 애플리케이션이 사용 | 아니요 |
| Message ID | String | 임의 메시지 ID | 아니요 |
| Correlation ID | String | 요청·응답 상관 관계 지원, 튜토리얼 6 참고 | 아니요 |
| Reply To | String | 응답 큐 이름 전달, 튜토리얼 6 참고 | 아니요 |
| Expiration | String | 메시지별 TTL | 아니요 |
| Timestamp | Timestamp | 애플리케이션 제공 타임스탬프 | 아니요 |
| User ID | String | 사용자 ID, 설정 시 검증됨 | 아니요 |
| App ID | String | 애플리케이션 이름 | 아니요 |
메시지의 type 속성은 애플리케이션이 어떤 종류의 메시지인지 소통하는 임의 문자열이에요. 퍼블리셔가 게시 시점에 설정해요. 값은 퍼블리셔와 컨슈머가 합의하는 도메인 특정 문자열이면 뭐든 돼요. RabbitMQ는 이 필드를 검증하거나 사용하지 않아요. 애플리케이션·플러그인이 해석하는 용으로 존재해요. 실제로 메시지 타입은 자연스럽게 그룹으로 나뉘고, 점으로 구분하는 명명 관례가 흔해요(RabbitMQ·클라이언트가 요구하는 건 아니지만). 예: orders.created, logs.line, profiles.image.changed. 컨슈머가 처리할 수 없는 타입의 전달을 받으면, 디버깅을 쉽게 하기 위해 그런 이벤트를 로그로 남기는 걸 강력히 권장해요.
콘텐츠(MIME 미디어) 타입과 콘텐츠 인코딩 필드는 퍼블리셔가 메시지 페이로드의 역직렬화·디코딩 방식을 컨슈머에게 알리는 데 써요. RabbitMQ는 이를 검증하거나 사용하지 않아요. 예를 들어 JSON 페이로드 메시지는 application/json을 쓰는 게 맞고, 페이로드가 LZ77(GZip)으로 압축되면 콘텐츠 인코딩이 gzip이어야 해요. 인코딩은 쉼표로 구분해 여러 개 지정할 수 있어요.
인정 모드
컨슈머를 등록할 때 애플리케이션은 두 전달 모드 중 하나를 선택할 수 있어요.
- 자동 (전달에 인정이 필요 없음, 일명 "fire and forget")
- 수동 (전달에 클라이언트 인정이 필요)
컨슈머 인정은 퍼블리셔 쪽의 밀접 관련 개념인 퍼블리셔 확인과 함께 별도 문서 가이드의 주제예요. 수동 인정 모드에서 컨슈머는 얼마나 많은 전달이 "in flight"(네트워크를 통해 이동 중이거나 전달됐지만 미인정)일 수 있는지 제한할 수 있어요. 이는 컨슈머 과부하를 피할 수 있어요. 이 기능은 컨슈머 인정과 함께 별도 문서 가이드의 주제예요.
컨슈머 용량
RabbitMQ 관리 UI와 모니터링 데이터 엔드포인트(Prometheus 스크래핑용 포함)는 개별 큐에 대해 컨슈머 용량(이전의 consumer utilisation)이라는 지표를 표시해요. 이 지표는 큐가 메시지를 컨슈머에게 즉시 전달할 수 있는 시간의 비율로 계산돼요. 운영자가 큐에 컨슈머(애플리케이션 인스턴스)를 더 추가할 만한 조건을 알아차리게 도와줘요. 이 값이 100%보다 작으면, 다음 경우에 큐 리더 복제본이 메시지를 더 빨리 전달할 수 있을지도 몰라요.
- 컨슈머가 더 많거나
- 컨슈머가 전달 처리에 시간을 덜 쓰거나
- 컨슈머 채널이 더 높은 prefetch 값을 썼거나
컨슈머 용량은 컨슈머가 없는 큐에서 0%가 돼요. 온라인 컨슈머가 있지만 메시지 흐름이 없는 큐는 100%가 돼요. 어떤 수의 컨슈머든 이런 전달 속도를 유지할 수 있다는 생각이에요. 컨슈머 용량은 단지 힌트일 뿐이에요. 컨슈머 애플리케이션은 크기 조정과 용량 변경에 도움이 되도록 운영에 관한 더 구체적인 지표를 모을 수 있고 또 그래야 해요.
컨슈머 취소
컨슈머를 취소하려면 그 식별자(컨슈머 태그)를 알아야 해요. 컨슈머가 취소되면 그 후로는 전달이 더 이상 발송되지 않아요. 이전에 발송된 "in flight" 전달이 여전히 있을 수 있다는 점에 주의하세요. 컨슈머를 취소한다고 그 전달들이 버려지거나 재큐잉되지는 않아요. 취소된 컨슈머는 RabbitMQ가 basic.cancel 메서드를 처리하는 시점에 in-flight였던 전달 외에는 어떤 새 전달도 관찰하지 않아요. 이전의 모든 미확인 전달은 어떤 방식으로든 영향받지 않아요. in-flight 전달을 재큐잉하려면 애플리케이션이 채널을 닫아야 해요.
폴링(basic.get)은 피하세요
danger: 이 섹션에서 설명하는 메커니즘은 폴링의 한 형태예요. 분산 시스템의 폴링 기반 접근이 늘 그렇듯 매우 비효율적이고, 특히 큐가 한동안 비어 있을 수 있는 경우에요. 통합 테스트 외에는 이 AMQP 0-9-1 소비 메커니즘을 강력히 반대해요.
RabbitMQ 관리·Prometheus 플러그인은 폴링(basic.get)을 쓰는 애플리케이션을 감지하는 데 도움이 되는 여러 지표를 제공해요.
tip: 폴링 대신 수명이 긴 컨슈머를 사용하세요.
AMQP 0-9-1에서 basic.get 프로토콜 메서드로 메시지를 하나씩 가져올 수 있어요. 메시지는 FIFO 순서로 가져와요. 컨슈머(구독)처럼 자동·수동 인정을 쓸 수 있어요. 메시지를 하나씩 가져오는 건 일반적인 수명이 긴 컨슈머에 비해 매우 비효율적이라 강력히 권장하지 않아요. 폴링 기반 알고리즘답게, 메시지 게시가 간헐적이고 큐가 오랜 기간 비어 있을 수 있는 시스템에서는 극도로 낭비적이에요. 망설여지면 일반적인 수명이 긴 컨슈머를 선호하세요.
전달 인정 타임아웃
important: RabbitMQ 4.3부터 전달 인정 타임아웃은 quorum 큐만 지원해요.
RabbitMQ는 컨슈머 전달 인정에 타임아웃을 적용해요. 이는 컨슈머가 메시지 전달을 인정하지 않는 상황을 감지하는 보호 메커니즘이에요. 전달 인정 타임아웃을 설정하면 온디스크 데이터 컴팩션을 방지하고 노드가 디스크 공간을 소진하는 것을 막는 데 도움이 돼요. 컨슈머가 타임아웃 값 안에 전달을 인정하지 않으면 그 채널이 PRECONDITION_FAILED 채널 예외로 닫혀요. 메시지는 이렇게 생겼어요.
Consumer 'consumer-tag-998754663370' on channel 1 and queue 'qq.1' in vhost '/' has timed out
waiting for a consumer acknowledgement of a delivery with delivery tag = 10. Timeout used: 180000 ms.
This timeout value can be configured, see consumers doc guide to learn more
이 오류는 컨슈머가 연결된 노드가 로그로 남겨요. 그 채널의 모든 컨슈머로부터의 이후 모든 전달은 재큐잉돼요. PRECONDITION_FAILED 채널 예외를 해결하려면 컨슈머를 재평가하고 타임아웃 값을 늘리는 걸 고려하세요.
RabbitMQ의 기본 타임아웃 값은 30분이에요. 타임아웃을 적용할지는 1분 간격으로 주기 평가해요. 1분보다 낮은 값은 지원되지 않고, 5분보다 낮은 값은 권장되지 않아요. 타임아웃 값은 rabbitmq.conf(밀리초 단위)에서 설정할 수 있어요.
# 30 minutes in milliseconds
consumer_timeout = 1800000
# one hour in milliseconds
consumer_timeout = 3600000
타임아웃은 advanced.config로 비활성화할 수 있지만 권장하지 않아요.
%% advanced.config
[
{rabbit, [
{consumer_timeout, undefined}
]}
].
타임아웃을 완전히 끄는 대신 높은 값(예: 몇 시간)을 쓰는 걸 고려하세요. RabbitMQ 3.12부터 타임아웃 값은 큐별로도 설정할 수 있어요. consumer-timeout 폴리시 키를 설정하세요. 값은 밀리초 단위여야 해요. 적용 여부는 1분 간격으로 주기 평가돼요.
# override consumer timeout for a group of queues using a policy
rabbitmqctl set_policy queue_consumer_timeout "with_delivery_timeout\.*" '{"consumer-timeout":3600000}' --apply-to quorum_queues
큐를 선언할 때 큐에 x-consumer-timeout 선택적 큐 인자를 설정할 수도 있어요. 타임아웃은 밀리초 단위예요. 적용 여부는 1분 간격으로 주기 평가돼요.
이런 컨슈머 누수(leak)가 발생하는 시나리오에서는 각 채널에서 활성화할 수 있는 컨슈머 수를 제한하는 것도 좋아요. rabbitmq.conf의 consumer_max_per_channel 설정으로 구성할 수 있어요.
consumer_max_per_channel = 100
컨슈머 독점성과 단일 활성 컨슈머
classic 큐만 해당되는데, AMQP 0-9-1 클라이언트로 컨슈머를 등록할 때 basic.consume 메서드의 exclusive 플래그를 true로 설정해 그 큐의 유일한 컨슈머가 되도록 요청할 수 있어요. 그 시점에 큐에 이미 등록된 컨슈머가 없을 때만 호출이 성공해요. 이러면 한 번에 한 컨슈머만 큐에서 소비하게 만들 수 있어요. 독점 컨슈머가 취소되거나 죽으면, 큐에서 계속 소비하게 새 컨슈머를 등록하는 건 애플리케이션 책임이에요. 독점 소비 그리고 소비 연속성이 모두 필요하면 단일 활성 컨슈머를 쓰세요.
important: quorum 큐는
basic.consume프레임의exclusive플래그를 무시해요. quorum 큐에서는 대신 단일 활성 컨슈머를 쓰세요.
**단일 활성 컨슈머(SAC)**는 한 번에 한 컨슈머만 큐에서 소비하게 만들어요. 활성 컨슈머가 취소되거나 연결이 끊기면 다른 등록된 컨슈머가 그 자리를 차지해요. 한 컨슈머만으로 소비하는 것은 메시지를 큐에 도착한 순서대로 소비·처리해야 할 때 유용해요(메시지 순서 보존 참고).
일반적인 이벤트 시퀀스는 이래요.
- 큐가 선언되고 일부 컨슈머가 거의 동시에 등록돼요
- 가장 먼저 등록된 컨슈머가 단일 활성 컨슈머가 돼요. 메시지는 그 컨슈머에게 발송되고 나머지 컨슈머는 무시돼요
- 큐가 quorum 큐이고 더 높은 우선순위의 새 컨슈머가 등록되면, 큐는 현재 활성 컨슈머에게 메시지 전달을 멈춰요. 모든 메시지가 인정되면 새 컨슈머가 활성 컨슈머가 돼요
- 단일 활성 컨슈머가 어떤 이유로 취소되거나 죽으면 다른 컨슈머가 활성 컨슈머로 선택돼요. 즉 큐가 다른 컨슈머로 자동 페일오버해요
단일 활성 컨슈머 기능을 끄면 메시지가 라운드 로빈으로 모든 컨슈머에게 발송된다는 점에 주의하세요. SAC는 큐를 선언할 때 x-single-active-consumer 인자를 true로 설정해 활성화할 수 있어요. AMQP 0-9-1 독점 컨슈머에 비해, 단일 활성 컨슈머는 소비 연속성을 유지하는 데 애플리케이션 측 부담이 덜해요. 컨슈머는 등록만 하면 되고 페일오버가 자동으로 처리되므로, 활성 컨슈머 실패를 감지하고 새 컨슈머를 등록할 필요가 없어요. 관리 UI와 CLI는 기능이 켜진 큐에서 현재 활성 컨슈머가 누구인지 알려줄 수 있어요.
AMQP 1.0 클라이언트가 quorum 큐에서 소비하면 AMQP 1.0 flow 프레임에 rabbitmq:active라는 링크 상태 속성을 받아요. SAC가 켜져 있으면 이 속성은 컨슈머가 활성인지(true) 비활성·대기인지(false)를 나타내요. SAC가 꺼져 있으면 값은 항상 true예요. 각 컨슈머는 이 속성을 받아요.
- 처음으로 크레딧을 부여한 뒤(초기 상태)
- 활동 상태가 바뀔 때마다(SAC가 켜져 있을 때만 관련)
이 기능은 AMQP 1.0 특정 기능이라 AMQP 0-9-1 클라이언트에는 없어요. 다음은 amqp10_client 라이브러리를 쓰는 Erlang 예시로 활동 상태 알림을 받는 예시예요.
%% Attach a receiver opting in to receive notifications on link state property changes.
{ok, Receiver} = amqp10_client:attach_link(
Session,
#{name => <<"my-receiver">>,
role => {receiver, #{address => <<"/queues/my-queue">>}, self()},
snd_settle_mode => unsettled,
notify_when_state_properties_changed => true}),
receive {amqp10_event, {link, Receiver, attached}} -> ok
end,
ok = amqp10_client:flow_link_credit(Receiver, 10, never),
%% Wait for the initial activity status notification.
receive {amqp10_event,
{link, Receiver,
{state_properties, #{<<"rabbitmq:active">> := Active}}}} ->
io:format("Consumer active: ~p~n", [Active])
end.
classic 큐와 함께 쓸 때 초기 활성 컨슈머는 컨슈머 우선순위를 쓰더라도 무작위로 선택돼요. 큐가 quorum 큐이고 더 높은 우선순위의 새 컨슈머가 등록되면, 큐는 현재 활성 컨슈머에게 전달을 멈추고, 모든 메시지가 인정되면 새 컨슈머가 활성 컨슈머가 돼요. 이 동작에 대해 더 알고 싶으면 이 기능을 발표한 블로그 포스트를 참고하세요. SAC와 함께 독점 컨슈머를 등록하려 하면 오류가 발생해요. SAC는 정의상 여러 컨슈머가 온라인일 것을 가정하니까요.
단일 활성 컨슈머 기능은 폴리시로 활성화할 수 없어요. RabbitMQ의 폴리시는 본질적으로 동적이라 왔다 갔다 하며 선언한 기능을 켜고 끌 수 있어요. 큐에서 단일 활성 컨슈머가 갑자기 꺼지는 상황을 상상해 보세요. 브로커가 비활성 컨슈머에게 메시지를 보내기 시작하고 메시지가 병렬로 처리돼, 단일 활성 컨슈머가 달성하려는 것과 정확히 반대가 돼요. 단일 활성 컨슈머의 의미론이 폴리시의 동적 성질과 잘 맞지 않으므로, 이 기능은 큐 선언 시 큐 인자로만 활성화할 수 있어요.
컨슈머 활동 플래그
관리 UI와 list_consumers CLI 명령은 컨슈머에 대해 active 플래그를 보고해요. 이 플래그 값은 몇 가지 파라미터에 따라 달라져요.
- classic 큐에서는 단일 활성 컨슈머가 켜져 있지 않으면 항상
true - quorum 큐에서 단일 활성 컨슈머가 켜져 있지 않으면 기본
true이고, 컨슈머가 연결된 노드가 다운된 것으로 의심되면false - 단일 활성 컨슈머가 켜져 있으면 플래그는 현재 단일 활성 컨슈머에 대해서만
true. 큐의 다른 컨슈머는 활성 컨슈머가 사라지면 승격되기를 기다리므로active가false
일반적으로 큐에 연결된 활성 컨슈머는 라운드 로빈 방식으로 큐에서 메시지를 받아요.
컨슈머 우선순위
컨슈머 우선순위는 활성 상태인 동안 높은 우선순위 컨슈머가 메시지를 받게 하고, 높은 우선순위 컨슈머가 효율적 prefetch 설정 등으로 막혀 있을 때만 낮은 우선순위 컨슈머에게 메시지가 가게 해요. 컨슈머 우선순위를 쓸 때 여러 활성 컨슈머의 우선순위가 같으면 메시지는 라운드 로빈으로 전달돼요. 컨슈머 우선순위는 별도 가이드에서 다뤄요.
예외 처리와 동시성
컨슈머는 전달 처리 중이거나 다른 컨슈머 연산 중 발생하는 예외를 처리할 것으로 기대돼요. 그런 예외는 로그로 남기고 모아서 무시해야 해요. 컨슈머가 의존성 부재 같은 이유로 전달을 처리할 수 없으면 명확히 로그로 남기고 다시 처리할 수 있을 때까지 스스로 취소해야 해요. 그러면 컨슈머의 비가용성이 RabbitMQ와 모니터링 시스템에 보이게 돼요.
컨슈머 동시성은 주로 클라이언트 라이브러리 구현 세부사항과 애플리케이션 설정의 문제예요. 대부분의 클라이언트 라이브러리(Java·.NET·Go·Erlang 등)에서 전달은 모든 비동기 컨슈머 연산을 처리하는 스레드 풀(또는 유사)에 발송돼요. 풀은 보통 제어 가능한 동시성 정도를 가져요. Java·.NET 클라이언트는 단일 채널의 전달이 동시성 정도와 무관하게 받은 순서대로 발송됨을 보장해요. 다만 일단 발송되면 전달의 동시 처리는 처리 스레드 사이에서 자연스러운 경쟁 조건을 만들 수 있어요. 일부 클라이언트(Bunny 등)·프레임워크는 전달이 동시에 처리될 때 자연스러운 경쟁 조건을 피하려고 컨슈머 발송 풀을 단일 스레드로 제한할 수 있어요. 일부 애플리케이션은 전달의 완전한 순차 처리를 요구하므로 동시성 계수를 1로 쓰거나 자신의 코드에서 동기화를 처리해야 해요. 전달을 동시에 처리할 수 있는 애플리케이션은 사용 가능한 코어 수까지 동시성을 쓸 수 있어요.
단일 RabbitMQ 큐는 단일 코어로 제한돼요. 노드의 CPU 활용을 높이려면 큐를 여러 개 쓰세요. 병렬성을 높이는 데 sharding·consistent hash exchange 같은 플러그인이 도움이 될 수 있어요.
더 알아보기
- 확인과 인정 (Confirms) — 수동 인정·재큐잉·prefetch 심화
- 큐 (Queues) — 소비의 대상이 되는 큐
- 안정성 가이드 — 재전송·데이터 안전성
- 익스체인지 (Exchange) — 메시지가 오는 경로