consume 채널 하나만 가져가야되는 이유

백엔드&인프라 추종자·2025년 11월 14일

정우님, 이 질문 핵심이 정말 중요합니다.
“그러면 consume도 worker마다 자기 전용 채널을 주면 안전한 것 아니냐?”
→ 기술적으로는 맞는 말처럼 보여요. 하지만 실제 RabbitMQ 구조에서는 문제가 크게 2가지 발생합니다.


✅ 1. 기술적으로 가능함.

worker마다:

ch := conn.Channel()
msgs := ch.Consume(...)
for msg := range msgs {
    handle(msg)
}

이렇게 하면 확실히 worker는 자기 채널만 사용하니까 goroutine-safe 문제가 없습니다.


❌ 2. 하지만 “consume 채널을 worker마다 따로 만드는 방식”은 RabbitMQ 구조상 거의 절대 쓰면 안 됩니다.

이유는 아래 2개가 진짜 핵심:


❌ 이유 1 — Consumer 수 = 채널 수

RabbitMQ는 채널마다 consumer 1명이 붙어버립니다.
즉, worker가 100개면 consumer도 100명.

그러면 메시지 분배 방식이:

RabbitMQ의 내부 라운드로빈으로 worker들에게 분산됨

이게 문제입니다.

✔ Worker Pool에서 원래 원하는 동작

메시지를 중앙에서 받고
→ worker 풀이 고르게 처리하게 하고
→ worker가 느리면 백프레셔가 걸리고
→ Ack도 worker 쪽에서 제어

✘ 하지만 RabbitMQ 소비자 100명이면?

  • RabbitMQ가 각 worker에게 메시지를 자동 분배해버림
    (workerChan 풀 제어 불가)
  • 느린 worker도 메시지를 받음 → backlog 쌓임
  • 빠른 worker는 쉬고 있음 → 리소스 낭비
  • worker 수 조절이 매우 어려워짐
  • 메시지 재처리, nack, requeue 제어가 안 됨

즉, Worker Pool을 구축할 수가 없음.
RabbitMQ가 강제로 라운드로빈 worker 스케줄러가 되어버림.


❌ 이유 2 — Ack 처리 타이밍 제어 불가

worker들이 바로 msg.Ack() 하면:

  • 빠른 worker는 벌써 100개 처리했는데
  • 느린 worker는 1개 처리 중
    → RabbitMQ는 느린 worker에게도 계속 메시지를 보내기 때문에 큐가 특정 worker에 쌓임.

worker pool 구조에서는 “느린 worker는 메시지를 적게 받고”, “빠른 worker는 여러 개 처리”해야 정상인데
RabbitMQ는 그런 걸 고려하지 않습니다.

즉, backpressure 불가능.


❌ 이유 3 — 메시지 처리량 스파이크에서 오히려 더 불안정

consumer 수가 많아지면:

  • RabbitMQ 내부 스레드 수 증가
  • 채널 수 증가
  • consumer bookkeeping 증가
  • 메시지 ack/nack 트래킹 비용 증가
  • broker 부하 증가 (특히 prefetch > 1이면 더 심각)

실전에서는 5~20명 정도만 consumer 붙이고, 나머지는 worker 풀로 처리합니다.


🔥 그래서 업계의 거의 유일한 정답 구조:

[RabbitMQ]
   ⬇ (하나의 channel / 하나의 consumer)
[Go goroutine 1개]
   ⬇ msgs → workerChan
[Worker Goroutine Pool N개]
   → 병렬 처리
   → Ack 여기서

이 구조가 전 세계 RabbitMQ consumer 구현의 정석입니다.


⭐ 그럼 worker마다 consume 채널을 쓰는 건 언제 쓰냐?

단 한 가지 경우만 유효:

각 worker는 서로 다른 queue를 consume 한다.

즉,

  • Queue가 A/B/C/D... 여러 개 있고
  • Worker가 서로 다른 Queue를 담당할 때

그럴 땐 channel 1개씩 줘도 됨.


🔚 최종 결론

질문답변
worker마다 consume 채널 한 개씩 줘도 되냐?기술적으로 YES
실전에서 그렇게 쓰면 되냐?거의 NO (강력 비권장)
문제점backpressure 불가, 전체 처리량 불안정, RabbitMQ 스케줄러가 제어권을 가져감
정답 구조consume 1개 + Go worker pool

정우님이 만약 ffmpeg 작업 큐파일 변환 작업 큐를 만들고 있다면
무조건 consumer 하나 + worker pool 구조가 가장 안정적입니다.

원하면 이 구조를 정우님 프로젝트에 맞는 실제 코드 형태로 만들어서 전달드릴까요?

profile
AI 답변 글을 주로 올립니다.

0개의 댓글