소개
소프트웨어 개발에서 신뢰할 수 있는 재시도 로직은 네트워크 문제나 임시 중단과 같은 간헐적 실패를 처리하는 데 필수적입니다. 최근에 한 개발자가 고정 시간 간격으로 실패한 작업을 재시도하는 for
루프를 사용한 코드베이스를 보았습니다. 이 접근 방식은 간단해 보일 수 있지만 실제 애플리케이션에서 필요한 회복력이 부족합니다. 그곳에서 지수 백오프가 등장합니다. 이는 재시도를 더 스마트하고 효율적으로 만들기 위해 설계된 전략입니다.
이 기사에서는 지수 백오프가 어떻게 작동하는지, 기본 재시도 루프에 비해 어떤 장점이 있는지, 그리고 시스템의 신뢰성을 높이기 위해 이를 어떻게 구현할 수 있는지 살펴보겠습니다. 또한 이메일 발송 모듈을 사용하여 지수 백오프를 활용해 보다 탄력적인 오류 처리를 보장하는 방법을 실용적인 예제로 안내하겠습니다.
지수 백오프란 무엇인가요?
지수 백오프는 재시도 시도 간의 대기 시간이 각 실패 후 기하급수적으로 증가하는 재시도 전략입니다. 고정 간격으로 재시도하는 대신, 각 후속 시도는 이전 시도보다 더 오래 기다립니다. 일반적으로 매번 지연 시간을 두 배로 늘립니다. 예를 들어, 초기 지연 시간이 1초라면 다음 재시도는 2, 4, 8초 등으로 진행됩니다. 이 접근 방식은 시스템의 부담을 줄이고 높은 수요 기간 동안 외부 서비스를 압도할 위험을 최소화하는 데 도움이 됩니다.
재시도 간의 시간을 더 많이 허용함으로써 지수 백오프는 임시 문제를 해결할 기회를 제공하여 보다 효율적인 오류 처리와 향상된 애플리케이션 안정성으로 이어집니다.
지수 백오프의 장단점
장점:
-
시스템 부하 감소: 재시도를 간격을 두고 수행함으로써, 지수 백오프는 서버가 압도당할 가능성을 최소화하며, 특히 속도 제한이나 일시적인 장애를 처리하는 데 유용합니다.
-
효율적인 오류 처리: 증가하는 지연은 일시적인 문제에 자연스럽게 해결될 수 있는 시간을 더 제공하여, 성공적인 재시도의 가능성을 향상시킵니다.
-
안정성 향상: 특히 높은 트래픽을 처리하는 시스템에서 재시도 시도가 쏟아지는 것을 방지하여, 과도한 자원 소비 없이 애플리케이션이 원활하게 실행되도록 합니다.
단점:
- 지연 증가: 각 재시도가 점점 더 오래 걸림에 따라, 지수 백오프는 지연을 초래할 수 있으며, 특히 성공하기 전에 많은 재시도가 필요한 경우 더욱 그렇습니다.
지수 백오프의 주요 사용 사례
지수 백오프는 시스템이 외부 서비스와 상호 작용하거나 대량의 트래픽을 관리하는 시나리오에서 특히 유용합니다. 다음은 다른 일반적인 사용 사례입니다:
-
요금 제한 API: 일부 API는 요금 제한이 있어 특정 시간 내에 요청을 제한합니다. 지수 백오프는 즉각적인 재시도를 피하는 데 도움을 주어 제한이 초기화될 시간을 제공합니다.
-
네트워크 불안정성: 일시적인 네트워크 장애나 타임아웃의 경우, 지수 백오프는 시도 간의 대기 시간을 늘려 네트워크가 안정화될 수 있도록 도와줍니다.
-
데이터베이스 연결: 부하가 많이 걸린 데이터베이스에 연결할 때, 지수 백오프는 재시도를 지연시켜 추가 과부하를 방지하고 데이터베이스가 회복할 시간을 제공합니다.
-
큐 시스템: 메시지 큐 시스템에서 메시지가 오류로 실패할 경우, 재시도에 지수 백오프를 사용하면 빠른 재처리를 방지하고 일시적인 문제를 해결할 시간을 줄 수 있습니다.
지수 백오프를 사용한 기본 이메일 발송 서비스 구축
지수 백오프를 시연하기 위해, 오류가 발생할 경우 이메일 발송을 재시도하는 기본 이메일 발송기를 구축할 것입니다. 이 예제는 단순한 for 루프와 비교하여 지수 백오프가 재시도 프로세스를 개선하는 방법을 보여줍니다.
import nodemailer from "nodemailer";
import { config } from "../common/config";
import SMTPTransport from "nodemailer/lib/smtp-transport";
const emailSender = async (
subject: string,
recipient: string,
body: string
): Promise<boolean> => {
const transport = nodemailer.createTransport({
host: config.EMAIL_HOST,
port: config.EMAIL_PORT,
secure: true,
auth: { user: config.EMAIL_SENDER, pass: config.EMAIL_PASSWORD },
} as SMTPTransport.Options);
const mailOptions: any = {
from: config.EMAIL_SENDER,
to: recipient,
subject: subject,
};
const maxRetries = 5; // maximum number of retries before giving up
let retryCount = 0;
let delay = 1000; // initial delay of 1 second
while (retryCount < maxRetries) {
try {
// send email
await transport.sendMail(mailOptions);
return true;
} catch (error) {
// Exponential backoff strategy
retryCount++;
if (retryCount < maxRetries) {
const jitter = Math.random() * 1000; // random jitter(in seconds) to prevent thundering herd problem
const delayMultiplier = 2
const backOffDelay = delay * delayMultiplier ** retryCount + jitter;
await new Promise((resolve) => setTimeout(resolve, backOffDelay));
} else {
// Log error
console.log(error)
return false; // maximum number of retries reached
}
}
}
return false;
};
지수 백오프 매개변수 조정
지수 백오프를 구현하는 것은 재시도 전략이 애플리케이션의 요구에 잘 맞도록 특정 매개변수를 조정하는 것을 포함합니다. 다음의 주요 매개변수는 재시도 메커니즘에서 지수 백오프의 동작과 성능에 영향을 미칩니다:
- 초기 지연
- 목적: 첫 번째 재시도 전에 대기 시간을 설정합니다. 즉각적인 재시도를 방지할 만큼 길어야 하지만 눈에 띄는 지연을 피할 만큼 짧아야 합니다.
- 추천 설정: 500 ms에서 1000 ms 사이의 지연으로 시작합니다. 중요 시스템의 경우 더 짧은 지연을 사용하고, 덜 긴급한 작업은 더 긴 지연을 가질 수 있습니다.
- 지연 배수기
-
목적: 각 재시도 후 지연이 얼마나 빨리 증가하는지를 제어합니다. 2의 배수기는 지연 시간을 두 배로 늘립니다(예: 1초, 2초, 4초).
-
추천 설정: 일반적으로 1.5와 2 사이의 배수기가 반응성과 안정성을 균형 있게 유지합니다. 시스템이 재시도 사이의 긴 지연을 처리할 수 있다면 더 높은 배수기(예: 3)가 적합할 수 있습니다.
- 최대 재시도 횟수
-
목적: 자원을 소모하거나 시스템 부하를 증가시킬 수 있는 과도한 재시도를 방지하기 위해 재시도 횟수를 제한합니다.
-
추천 설정: 대부분의 애플리케이션에는 3에서 5회의 재시도가 일반적으로 충분합니다. 이 이상으로 넘어가면 작동이 실패로 기록되거나 사용자에게 알림을 보내거나 경고를 발생시키는 등 다른 방식으로 관리되어야 할 수 있습니다.
- 지터 (무작위화)
- 목적: 각 지연에 무작위성을 추가하여 재시도가 집중되는 것을 방지하고 천둥 무리 효과를 초래하지 않도록 합니다.
- 추천 설정: 각 재시도 간격에 0에서 500ms 사이의 무작위 지연을 추가합니다. 이 지터는 재시도 시도를 시간이 지남에 따라 더 고르게 분산시키는 데 도움을 줍니다.
결론
지수 백오프를 사용함으로써 애플리케이션에 회복력을 추가하여 예기치 않은 문제를 처리할 준비를 합니다. 이는 작은 변화이지만 큰 영향을 미치며, 특히 애플리케이션이 성장함에 따라 더욱 그렇습니다.
그럼 지금은 여기까지입니다, 여러분. 댓글을 남기고 질문이 있으면 언제든지 물어보세요. 더 신뢰할 수 있고 회복력 있는 앱을 만드는 것에 건배합니다.
행복한 코딩 되세요! 👨💻❤️
Source:
https://timothy.hashnode.dev/implementing-exponential-backoff-for-reliable-systems