در سیستمهای پیچیده که به سرویسهای خارجی یا دیتابیسهای توزیعشده وابسته هستند، خطاهای موقتی مانند قطعی شبکه یا بار زیاد بر روی سرورها به طور مداوم رخ میدهند. این مشکلات ممکن است منجر به شکست درخواستها و از دست رفتن تجربه کاربری شوند. یکی از بهترین راهکارها برای مدیریت این خطاها، استفاده از مکانیزمهای Retry (تلاش مجدد) و Exponential Backoff (تاخیر نمایی) است. در این مقاله، به تشریح این مکانیزم و پیادهسازی آن در Nest.js و سایر سیستمهای پیچیده مالی مانند PayPal و AWS S3 میپردازیم.
نکته :در تهیه این مقاله از هوش مصنوعی کمک گرفته شده است.
Retry Mechanism چیست؟
مکانیزم Retry به شما این امکان را میدهد که در صورت شکست درخواست، سیستم به صورت خودکار تلاش مجدد برای ارسال همان درخواست انجام دهد. این رویکرد به خصوص در سیستمهای حساس به قطع ارتباط یا ازدحام ترافیک، بسیار موثر است.
در صورتی که درخواست شما به دلیل مشکلات موقت (مانند اختلال در شبکه یا بار زیاد بر روی سرورها) شکست بخورد، با استفاده از Retry Mechanism میتوانید چندین بار تلاش مجدد انجام دهید تا احتمال موفقیت درخواست افزایش یابد.
Exponential Backoff چیست؟
مکانیزم Exponential Backoff به شما این امکان را میدهد که پس از هر شکست، زمان بین تلاشهای مجدد را به صورت نمایی افزایش دهید. به جای اینکه به صورت مداوم و بلافاصله درخواستها را ارسال کنید (که میتواند بار اضافی بر سرورها ایجاد کند)، سیستم به تدریج فاصله زمانی بین تلاشها را بیشتر میکند.
مثال:
- پس از اولین شکست، سیستم ۱ ثانیه صبر میکند.
- اگر شکست دوم رخ دهد، زمان انتظار به ۲ ثانیه افزایش مییابد.
- سپس ۴ ثانیه، ۸ ثانیه، و به همین ترتیب تا زمانی که یا درخواست موفق شود یا به حد مجاز تعداد تلاشها برسد.
فرض کنید که میخواهید به یک سرویس خارجی از طریق یک API درخواست ارسال کنید و در صورت شکست، با استفاده از Retry Mechanism و Exponential Backoff تلاش مجدد انجام شود. در Nest.js میتوانیم این مکانیزم را به کمک کتابخانههای Axios و RxJS پیادهسازی کنیم.
نصب کتابخانههای مورد نیاز
ابتدا کتابخانههای Axios و RxJS را نصب کنید:
npm install axios rxjs
```
پیادهسازی سرویس Retry در Nest.js
در این مثال، یک سرویس را پیادهسازی میکنیم که یک درخواست HTTP به یک سرویس خارجی ارسال میکند. در صورت بروز خطا، سیستم تلاش میکند درخواست را مجدداً ارسال کند و با هر شکست، زمان بین تلاشها به صورت نمایی افزایش مییابد.
import { Injectable, HttpService, InternalServerErrorException } from '@nestjs/common';
import { AxiosError } from 'axios';
import { catchError, retryWhen, delay, take, scan } from 'rxjs/operators';
import { throwError } from 'rxjs';
@Injectable()
export class RetryService {
constructor(private readonly httpService: HttpService) {}
private maxRetries = 5; // تعداد تلاشهای مجدد
private delayMs = 1000; // تاخیر اولیه در میلیثانیه
async fetchData(url: string) {
return this.httpService.get(url).pipe(
retryWhen(errors =>
errors.pipe(
scan((retryCount, error) => {
if (retryCount >= this.maxRetries) {
throw error; // اگر تعداد تلاشها از حد مجاز بیشتر شد، خطا برگردانده شود
}
retryCount++;
const backoffDelay = this.delayMs * Math.pow(2, retryCount); // Exponential Backoff
console.log(`Retrying... Attempt #${retryCount} after ${backoffDelay} ms`);
return retryCount;
}, 0),
delay(this.delayMs), // اعمال تاخیر بین تلاشها
take(this.maxRetries), // تعداد حداکثر تلاشها
),
),
catchError((error: AxiosError) => {
console.error(`Failed after ${this.maxRetries} retries`);
return throwError(() => new InternalServerErrorException('Request failed'));
}),
).toPromise();
}
}
نکات کلیدی پیادهسازی:
1. retryWhen و delay: این دو متد از RxJS برای مدیریت تلاشهای مجدد و تاخیر بین هر تلاش استفاده میکنند. `retryWhen` به شما این امکان را میدهد که تعداد تلاشها و زمانهای بین آنها را کنترل کنید.
2. Exponential Backoff: زمان بین تلاشها به صورت نمایی افزایش مییابد (۱ ثانیه، ۲ ثانیه، ۴ ثانیه و ...).
3. مدیریت خطا: پس از تعداد مشخصی تلاش، اگر هنوز مشکل وجود داشته باشد، سیستم خطای نهایی را برمیگرداند و تلاشها متوقف میشوند.
کنترلر برای استفاده از سرویس
حال میتوانید این سرویس را در یک کنترلر استفاده کنید:
import { Controller, Get } from '@nestjs/common';
import { RetryService } from './retry.service';
@Controller('retry')
export class RetryController {
constructor(private readonly retryService: RetryService) {}
@Get()
async handleRequest() {
const url = 'https://example.com/api';
try {
const response = await this.retryService.fetchData(url);
return response.data;
} catch (error) {
throw error;
}
}
}
1. PayPal و سیستم پرداخت بینالمللی
در سیستمهای مالی مانند PayPal، قطع ارتباط موقت بین سرورها و بانکها میتواند تراکنشها را دچار مشکل کند. PayPal از Exponential Backoff برای مدیریت این مشکلات استفاده میکند. اگر تراکنش به دلیل مشکلات شبکه قطع شود، سیستم به طور خودکار تلاش مجدد میکند، و با هر بار شکست، زمان بیشتری بین تلاشها قرار میدهد تا از بار اضافی بر سرور بانکها جلوگیری شود.
2. AWS S3 و مدیریت آپلود
در سیستم AWS S3، زمانی که کاربران فایلهای بزرگی را آپلود میکنند، ممکن است به دلیل مشکلات شبکه یا ازدحام سرور، آپلود موقتاً قطع شود. AWS S3 از Retry Mechanism و Exponential Backoff برای مدیریت این مشکلات استفاده میکند و تضمین میکند که فایلها حتی با وجود مشکلات موقتی شبکه، به درستی آپلود شوند.
3. Google Cloud و API درخواستها
Google Cloud نیز از Exponential Backoff برای مدیریت درخواستهای API خود استفاده میکند. اگر درخواستها به دلیل مشکلات شبکه یا ازدحام سرور شکست بخورند، Google Cloud با استفاده از Retry Mechanism و تاخیر نمایی تلاش مجدد میکند تا سرورها تحت فشار قرار نگیرند.
مزایا:
1. کاهش فشار بر سرورها: با افزایش تدریجی زمان بین تلاشها، سرورها کمتر تحت فشار قرار میگیرند.
2. افزایش احتمال موفقیت: مشکلات موقت شبکه با زمان بهبود مییابند و Retry Mechanism با Exponential Backoff احتمال موفقیت درخواستها را افزایش میدهد.
3. بهبود تجربه کاربری: کاربر نیازی به انجام دوباره درخواست ندارد؛ سیستم به صورت خودکار تلاش میکند تا خطا را رفع کند.
معایب:
1. تأخیر در پاسخگویی: اگر زمانهای Backoff به درستی تنظیم نشوند، تأخیر زیاد در تایید درخواست ممکن است منجر به نارضایتی کاربران شود.
2. مصرف منابع: هر تلاش مجدد نیاز به مصرف منابع سرور دارد و اگر به درستی تنظیم نشود، ممکن است منجر به هدررفت منابع شود.
3. پیچیدگی پیادهسازی: پیادهسازی درست این مکانیزم در سیستمهای توزیعشده، نیازمند تنظیمات پیچیده و هماهنگی بین اجزا است.
مکانیزمهای Retry و Exponential Backoff به ویژه در سیستمهای پیچیده و توزیعشده، راهحلی موثر برای مدیریت خطاهای موقتی و جلوگیری از بار اضافی بر روی سرورها هستند. پیادهسازی آنها در Nest.js، با استفاده از ابزارهای مناسب مانند Axios و RxJS، به توسعهدهندگان کمک میکند تا سیستمهای قابل اعتماد و مقاوم در برابر خطا ایجاد کنند