Retry 정책시 고려사항 (feat. Backoff 전략, DLT(DLQ))
Backoff 재시도 전략: 고정, 증가, 랜덤 Backoff 전략을 비교하고 장단점을 분석하여 시스템 부하를 효율적으로 관리합니다.
Retryable과 Unretryable 오류 구분: 재시도 가능한 오류와 불가능한 오류를 구분하여 적절한 재시도 전략을 수립합니다.
Dead Letter Topic (DLT): 여러 번의 재시도 후에도 처리되지 않는 메시지를 DLT로 이동시켜 추후 분석 및 수동 처리를 용이하게 합니다. Poison Pill 메시지도 DLT로 관리합니다.
Kafka를 사용하면서 메시지 처리의 신뢰성과 내구성을 보장하기 위해 재시도(retry) 정책을 설정하는 것은 매우 중요합니다. 이 블로그에서는 Kafka의 Retry 정책, 특히 randomized backoff
재시도와 DLT(Dead Letter Topic) 설정에 대해 다룹니다.
Kafka의 Retry 정책은 메시지 처리 실패 시 자동으로 재시도하는 메커니즘을 의미합니다. 이를 통해 일시적인 오류나 네트워크 문제로 인해 메시지가 유실되는 것을 방지할 수 있습니다. Kafka는 기본적으로 At-Least-Once 메시지 전달을 보장하지만, 재시도 정책을 통해 더 높은 신뢰성을 구현할 수 있습니다.
이를 위해 Backoff 전략과, DLT에 대해 고민해 볼 수 있습니다.
Kafka의 재시도 정책에는 다양한 Backoff 전략이 있습니다. 각 전략은 재시도 간격을 다르게 설정하여 시스템에 미치는 영향을 최소화하고 효율성을 극대화합니다.
고정 Backoff는 일정한 시간 간격으로 재시도하는 전략입니다.
@Retryable(value = Exception.class, maxAttempts = 3, backoff = @Backoff(delay = 2000))
public void consume(String message) throws Exception {
// 메시지 처리 로직
}
증가 Backoff는 재시도 간격이 점차 길어지는 전략입니다. 일반적으로 첫 재시도는 짧은 간격으로 시작하고, 이후 재시도 간격이 지수적으로 증가합니다.
@Retryable(value = Exception.class, maxAttempts = 3, backoff = @Backoff(delay = 2000, multiplier = 2))
public void consume(String message) throws Exception {
// 메시지 처리 로직
}
랜덤 Backoff는 재시도 간격을 랜덤하게 설정하여, 여러 재시도 요청이 동시에 발생하는 것을 방지하는 전략입니다.
@Retryable(value = Exception.class, maxAttempts = 3, backoff = @Backoff(delay = 1000, maxDelay = 5000, random = true))
public void consume(String message) throws Exception {
// 메시지 처리 로직
}
각 Backoff 전략은 다양한 시나리오에서 사용될 수 있으며, 각 전략의 장단점은 다음과 같습니다.
Backoff 전략 | 장점 | 단점 |
---|---|---|
고정 Backoff | - 구현이 간단하며 예측 가능성 높음 - 일정한 간격으로 재시도하여 시스템 자원 관리 용이 | - 시스템 부하가 집중될 수 있음 - 동일한 간격으로 재시도 시 일시적인 문제를 해결하기 어려울 수 있음 |
증가 Backoff | - 초기 재시도는 빠르게 시도하고, 이후 재시도는 점차 간격을 늘려 시스템 부하를 줄임 - 일시적인 문제 해결에 유리 | - 재시도 간격이 너무 길어질 수 있어 응답 시간이 길어질 수 있음 - 구현의 복잡성이 다소 증가 |
랜덤 Backoff | - 재시도 간격이 랜덤하게 분산되어 시스템 부하를 줄일 수 있음 - 여러 소비자가 동시에 재시도하는 문제를 방지 | - 랜덤성으로 인해 재시도 지연 발생 가능 - 재시도 간격 예측 어려움 |
💡TIP!
왜 Fixed Backoff와 Exponential Backoff는 재시도시 부하가 집중될 수 있을까요?
-> 처음 재시도 하는 시점이 똑같다면, 매 재시도마다 같은 시간때에 재시도 되기 때문입니다.
모든 오류가 재시도 가능한 것은 아닙니다. 오류의 유형에 따라 재시도 전략을 다르게 수립할 필요가 있습니다.
재시도 가능한 오류는 일시적인 문제로, 재시도를 통해 성공할 가능성이 있는 오류입니다. 예를 들어, 네트워크 타임아웃, 일시적인 서비스 중단 등이 있습니다.
@Retryable(
value = {TransientException.class},
maxAttempts = 3,
backoff = @Backoff(delay = 1000)
)
public void consume(String message) throws TransientException {
// 메시지 처리 로직
if (someTemporaryCondition()) {
throw new TransientException("Temporary issue, retrying...");
}
}
재시도 불가능한 오류는 재시도를 통해서도 해결되지 않는 오류입니다. 예를 들어, 데이터 유효성 검사 실패, 권한 오류, DeduplicationError 등이 있습니다.
@KafkaListener(topics = "your-topic", groupId = "your-group")
public void consume(String message) {
try {
// 메시지 처리 로직
if (invalidData(message)) {
throw new InvalidDataException("Invalid data, not retrying.");
}
} catch (InvalidDataException e) {
// DLT로 메시지 전송 로직
sendToDLT(message);
}
}
DLT는 여러 번의 재시도 후에도 처리되지 않는 메시지를 별도의 토픽에 저장하여 나중에 [수동으로 처리/ 로직 수정후 재배포]할 수 있도록 하는 방법입니다. 이를 통해 재시도 가능한 오류와 재시도 불가능한 오류를 구분할 수 있습니다.
Poison Pill
Poison Pill은 처리할 수 없는 메시지를 의미하며, 이 메시지를 계속 재시도하면 시스템에 부하를 줄 수 있습니다. 이런 메시지는 DLT로 보내어 나중에 분석하고 처리합니다.
Spring Kafka에서 @DltHandler
어노테이션을 사용하여 DLT 로직을 쉽게 구현할 수 있습니다.
import org.springframework.kafka.annotation.KafkaListener;
import org.springframework.kafka.annotation.DltHandler;
import org.springframework.kafka.annotation.RetryableTopic;
import org.springframework.kafka.annotation.Backoff;
import org.springframework.stereotype.Service;
@Service
public class KafkaConsumerService {
@KafkaListener(topics = "your-topic", groupId = "your-group")
@RetryableTopic(attempts = 3, backoff = @Backoff(delay = 1000, maxDelay = 5000, random = true), dltTopicSuffix = "-dlt")
public void consume(String message) throws Exception {
try {
// 메시지 처리 로직
processMessage(message);
} catch (Exception e) {
// 처리 중 오류 발생 시 예외를 던져 재시도 트리거
throw e;
}
}
@DltHandler
public void handleDlt(String message) {
// DLT로 메시지 처리 로직
System.out.println("DLT로 이동된 메시지: " + message);
// 추가 처리 로직 (예: 알림 발송, 로깅 등)
}
private void processMessage(String message) {
// 메시지 처리 로직
}
}
Kafka의 Retry 정책은 메시지 처리의 신뢰성과 내구성을 높이는 중요한 방법입니다.
다양한 Backoff 전략을 통해 시스템 부하를 효율적으로 관리하고,
DLT를 통해 재시도 불가능한 메시지를 효과적으로 관리할 수 있습니다.
또한, 재시도 가능한 오류와 불가능한 오류를 구분하여 적절한 대응을 할 수 있습니다.
각 방법의 장단점을 고려하여 시스템의 요구사항에 맞는 최적의 재시도 전략을 선택하는 것이 중요합니다.
이 블로그 포스트가 Kafka의 재시도 정책을 이해하고 구현하는 데 도움이 되기를 바랍니다.