경쟁 소비자 패턴(Competing Consumers Pattern)은 여러 소비자 인스턴스가 동일 메시지 큐에서 경쟁적으로 메시지를 가져와 병렬 처리하는 패턴이다. 처리량 확장과 부하 분산에 효과적이다.
구조
생산자들 → [메시지 큐] → 소비자 1 (처리중: M1)
→ 소비자 2 (처리중: M2)
→ 소비자 3 (처리중: M3)
각 메시지는 하나의 소비자만 처리 (중복 없음)
처리 완료 시 ACK → 큐에서 삭제
처리 실패 시 NACK → 큐로 반환 또는 DLQ
RabbitMQ 구현
javascript
// 생산자
const channel = await connection.createChannel();
await channel.assertQueue('email-tasks', { durable: true });
channel.sendToQueue(
'email-tasks',
Buffer.from(JSON.stringify({ to: 'user@example.com', subject: 'Hello' })),
{ persistent: true } // 메시지 지속성
);
// 소비자 (여러 인스턴스 실행)
await channel.prefetch(1); // 한 번에 1개씩만 받음
channel.consume('email-tasks', async (msg) => {
if (!msg) return;
const task = JSON.parse(msg.content.toString());
try {
await sendEmail(task);
channel.ack(msg); // 성공
} catch (err) {
channel.nack(msg, false, false); // 실패 → DLQ
}
});
Kafka 토픽 (파티션 3개):
파티션 0 → 소비자 A
파티션 1 → 소비자 B
파티션 2 → 소비자 C
소비자 수 > 파티션 수 → 일부 소비자 유휴
소비자 수 < 파티션 수 → 한 소비자가 여러 파티션
// Spring Kafka
@KafkaListener(topics = "email-tasks",
groupId = "email-processor",
concurrency = "3") // 3개 소비자 스레드
public void processEmail(EmailTask task) {
emailService.send(task);
}
처리량 확장
소비자 수 = ceil(목표 TPS / 소비자당 TPS)
예:
목표: 1000 이메일/초
소비자 1개: 50 이메일/초
필요 소비자 수: 1000/50 = 20개
Kubernetes HPA로 자동 확장:
큐 깊이 > 1000 → 소비자 Pod 스케일 아웃
관련 개념