페이지 불러오는 중
처리량이 부족해 토픽의 파티션 수를 늘리면(예: 12 → 24), 컨슈머 병렬도가 올라가 처리량은 개선됩니다. 그러나 다음과 같은 부작용이 동시에 나타납니다.
partition = hash(key) % numPartitions. numPartitions가 바뀌면 대부분의 키가 다른 파티션으로 재매핑됩니다.RangeAssignor/RoundRobin은 Eager(전체 취소 후 재할당) → 재할당 동안 전 컨슈머가 소비 중단.파티션 24개 = 최대 24개 컨슈머 동시 소비. 그러나 이들이 같은 DB/외부 API를 때리면 커넥션 풀·락 경합·rate limit이 새 병목이 됩니다. (스레드 수만 늘린 것과 동일한 함정)
연속성이 연계되는 작업(같은 그룹의 메시지가 반드시 순서대로 처리되어야 하는 발송 시나리오)에서는, 리밸런싱 튜닝에 기대기보다 "애초에 흐트러지지 않게" 강제하는 편이 안전합니다.
① 파티션 ID 고정 — 같은 그룹은 같은 파티션으로 순차 인입// groupId를 파티션에 직접 매핑 (파티션 수가 바뀌어도 매핑이 흔들리지 않도록 명시적 고정)
int partition = partitionRegistry.resolve(msg.getGroupId()); // 그룹→파티션 고정 테이블
kafkaTemplate.send(new ProducerRecord<>("mail-send", partition, msg.getGroupId(), payload));hash(key) % N에 맡기지 않고, 그룹 → 파티션 매핑을 레지스트리로 고정합니다. 파티션 증설이 있어도 기존 그룹의 순서 경로는 유지됩니다.MQ로 publish 하기 전에, 해당 groupId의 직전 메시지가 순차적으로 처리 완료되었는지 먼저 확인하고 다음 메시지를 보냅니다.
public void publishNext(GroupMessage msg) {
GroupProgress p = progressRepo.get(msg.getGroupId());
// 직전 시퀀스가 '처리 완료' 상태일 때만 다음 메시지 발행 (아니면 대기/재시도 큐)
if (p.getLastAckedSeq() + 1 != msg.getSeq()) {
pendingRepo.hold(msg); // 아직 차례 아님 → 보류
return;
}
kafkaTemplate.send(record(msg));
progressRepo.markInFlight(msg.getGroupId(), msg.getSeq());
}
// 컨슈머가 처리 완료 후 ack → progressRepo.markAcked(groupId, seq) → 보류분 재평가실무 선택: ①+②를 함께 적용하는 경우가 많습니다. 파티션 고정으로 "경로"를 고정하고, 발행 전 확인으로 "그룹당 동시 in-flight 1건"을 강제하면, 리밸런싱 세부 튜닝은 부차적 문제가 됩니다.
파티션 이동으로 순서가 깨질 수 있음을 전제로, 소비 측에서 순서 역전을 흡수합니다.
// 이벤트에 단조 증가 버전(또는 발생 시각) 포함 → 과거 이벤트는 무시
@KafkaListener(topics = "user-events", concurrency = "6")
public void onEvent(UserEvent e) {
int applied = stateRepo.getVersion(e.getUserId()); // 현재 반영된 버전
if (e.getVersion() <= applied) return; // stale → 스킵 (멱등)
stateRepo.applyIfVersionMatches(e.getUserId(), e.getVersion(), applied); // CAS 업데이트
}WHERE version = :expected 조건부 업데이트(CAS)로 동시 처리 시 한쪽만 성공하게 함.3.2에서 그룹당 in-flight를 1건으로 강제하면 리밸런스로 인한 순서 붕괴 위험은 크게 줄어듭니다. 아래는 처리량·중복을 줄이기 위한 보조 튜닝입니다.
concurrency(파티션 병렬도)와 별개로, DB/외부 호출부를 Semaphore·bounded pool로 감싸 동시 요청 수를 제한.hash(key) % N의 N을 바꾸는 순간 대부분의 키가 이동하고, 그 경계에서 순서·원자성 가정이 깨집니다.CooperativeStickyAssignor·수동 커밋·static membership은 부차적 안전장치가 됩니다.
댓글
GitHub 계정으로 의견이나 질문을 남길 수 있습니다.