백엔드 면접 용어 사전
메시지 큐QoS · basic.qos

prefetch

RabbitMQ에서 소비자당 미리 밀어 줄 미확인 메시지 수의 상한. 처리량과 공평한 분배 사이의 손잡이다.

RabbitMQ가 소비자 하나에게 확인(ack)받지 않은 메시지를 몇 개까지 미리 보낼지의 상한. basic.qos로 설정해서 QoS라고도 부른다.

먼저 — ack 모델

RabbitMQ는 Kafka와 달리 브로커가 전달 상태를 추적한다.

브로커 → 메시지 전달 → 소비자
                        │ 처리
                        ↓
브로커 ← basic.ack ──────┘   이제 브로커가 메시지를 지운다

소비자가 ack 없이 죽으면 → 브로커가 다른 소비자에게 재전달

처리 실패를 명시하려면 basic.nack이나 basic.reject를 보낸다(재큐 여부를 지정할 수 있다).

prefetch가 조절하는 것

prefetch = 1 소비자는 한 번에 미확인 메시지 1개만 보유 ack를 보내야 다음 것이 온다

  • 공평하게 분배되지만 매 건 왕복 대기 → 처리량 손해

prefetch = 1000

  • 브로커가 1,000개를 한꺼번에 밀어 준다
  • 왕복이 줄어 처리량은 좋지만,
    • 빠른 소비자가 1,000건을 쥔 채 느리게 처리하면
    • 다른 소비자는 놀고 부하 분산이 무너진다

즉 prefetch는 처리량과 공평한 분배 사이의 손잡이다. 메시지 처리 시간이 길고 편차가 크면 작게, 짧고 균일하면 크게 잡는 게 방향이다.

더 중요한 문제 — 메모리

미리 받은 메시지는 소비자 프로세스의 메모리에 올라간다. prefetch를 크게 잡고 메시지가 크면 소비자가 OOM으로 죽을 수 있다. 그리고 죽는 순간 쥐고 있던 미확인 메시지 전부가 재전달되므로, 다른 소비자로 부하가 한꺼번에 몰린다.

Kafka에는 왜 이 개념이 없나

Kafka는 파티션이 이미 소비 단위를 나눠 두기 때문이다. 한 파티션은 그룹 안에서 한 컨슈머만 읽으니, "누가 얼마나 가져갈지"를 따로 조율할 필요가 없다. 대신 Kafka에는 max.poll.records(한 번에 가져올 레코드 수) 같은 배치 크기 설정이 있는데, 이건 분배 조절이 아니라 배치 튜닝이다.

함께 보는 것

소비자가 메시지를 받고 오래 ack하지 않으면 consumer timeout에 걸려 연결이 끊길 수 있다. 처리 시간이 긴 작업이라면 이 값도 함께 조정해야 한다.

면접 함정

  • "prefetch는 크게 잡을수록 좋다" → 부하 분산이 무너지고 메모리를 먹는다.
  • "prefetch가 Kafka의 파티션과 같은 개념" → 분배 단위가 아니라 미확인 메시지 상한이다.

설정과 감각

// 소비자당 미확인 메시지 상한
channel.basicQos(20);          // prefetch = 20
channel.basicConsume("orders", false /* autoAck 끄기 */, consumer);

처리 시간이 길고 편차가 크다 (예: 건당 1~30초)

  • 작게 (1~5). — 큰 값이면 느린 소비자가 메시지를 쥐고 앉는다

처리 시간이 짧고 균일하다 (예: 건당 5ms)

  • 크게 (100~수백). — 작으면 왕복 대기가 처리량을 지배한다

자동 ack와 헷갈리지 말 것

channel.basicConsume("orders", true, consumer);   // autoAck = true

autoAck=true전달되는 즉시 확인 처리되어 prefetch가 사실상 무의미해지고, 소비자가 처리 도중 죽으면 메시지가 그대로 사라진다. 처리 보장이 필요한 큐에서는 반드시 꺼야 한다.

함께 보면 좋은 용어

노트에서 맥락과 함께 보기 — 메시지 큐·비동기 — Kafka/RabbitMQ·EOS·순서·outbox