ابزار مناسب برای پردازشهای پسزمینه (Background Tasks) در .NET
در این مقاله، سه گزینه اصلی یعنی BackgroundService (توسعهیافته توسط مایکروسافت)، Quartz.NET و Hangfire را با دیدگاه فنی و عملیاتی مقایسه میکنیم و در نهایت با یک مثال واقعی، پیادهسازی این سناریو را بررسی خواهیم کرد.
آشنایی با رقبا
الف) BackgroundService (ساده و بومی)
این کلاس پایه در .NET برای پیادهسازی IHostedService استفاده میشود. این سرویس مستقیماً در داخل چرخه حیات اپلیکیشن اجرا میشود.
-
مناسب برای: کارهای بسیار ساده، مانیتورینگهای داخلی سبک.
-
نقاط ضعف: عدم وجود مدیریت خطا (Retry) بهصورت پیشفرض، نبود پنل مدیریت، و از دست رفتن کارها در صورت ریستارت شدن سرور (چون دادهها در حافظه هستند).
ب) Quartz.NET (زمانبندی پیشرفته)
یک فریمورک قدیمی و قدرتمند که تمرکز اصلی آن بر روی زمانبندی (Scheduling) است.
-
مناسب برای: زمانبندیهای پیچیده (مثلاً: آخرین جمعه هر ماه، ساعت ۱۰ شب).
-
نقاط ضعف: پیکربندی آن نسبت به بقیه کمی دشوارتر است و پنل مدیریتی رسمی و بصری قدرتمندی (مانند Hangfire) ندارد.
ج) Hangfire (پادشاه قابلیت اطمینان و راحتی)
محبوبترین کتابخانه برای مدیریت Jobها در .NET که از پایگاه داده برای ذخیره وضعیت استفاده میکند.
-
مناسب برای: ارسال ایمیل/پیامک انبوه، پردازش تصویر، و هر کاری که نباید گم شود.
-
نقاط قوت: پنل مدیریتی (Dashboard) بسیار عالی، سیستم Retry خودکار، پشتیبانی از پایگاه داده (Persistence).
سناریو واقعی: ارسال ۳۰۰۰ پیامک
فرض کنید لیستی از ۳۰۰۰ شماره تلفن دارید و میخواهید پیامی را برای آنها ارسال کنید. اگر این کار را در یک درخواست HTTP معمولی انجام دهید، کاربر با Timeout مواجه میشود.
چرا در این سناریو Hangfire برنده است؟
برای ارسال ۳۰۰۰ پیامک، ما به Job-based Processing نیاز داریم. دلایل برتری Hangfire در اینجا عبارتند از:
-
Persistence: اگر هنگام ارسال پیامک ۱۰۰۰اُم، برق سرور قطع شود، Hangfire پس از بالا آمدن دوباره، دقیقاً میداند که کدام پیامها ماندهاند.
-
Retries: اگر پنل پیامک لحظهای قطع شود، Hangfire بهصورت خودکار دوباره تلاش میکند.
-
Observability: شما در پنل میبینید که چند نفر پیامک را دریافت کردهاند و چند نفر با خطا مواجه شدهاند.
پیادهسازی عملی با استفاده از Hangfire
در این بخش، مدل Job-based را برای ارسال ۳۰۰۰ پیامک پیاده میکنیم.
مرحله ۱: نصب و پیکربندی
ابتدا پکیجهای لازم را نصب کنید:
dotnet add package Hangfire
در فایل Program.cs:
builder.Services.AddHangfire(config => config
.UseSqlServerStorage(builder.Configuration.GetConnectionString("HangfireDb")));
builder.Services.AddHangfireServer(); // اضافه کردن ورکر
مرحله ۲: تعریف سرویس ارسال پیامک
public interface ISmsService
{
Task SendBulkSms(List phoneNumbers, string message);
Task SendSingleSms(string phoneNumber, string message);
}
public class SmsService : ISmsService
{
public async Task SendSingleSms(string phoneNumber, string message)
{
// منطق اتصال به پنل پیامک (مثلاً KavehNegar یا غیره)
Console.WriteLine($"Sending SMS to {phoneNumber}: {message}");
await Task.Delay(100); // شبیهسازی تاخیر شبکه
}
public async Task SendBulkSms(List phoneNumbers, string message)
{
foreach (var phone in phoneNumbers)
{
// ایجاد یک Job جداگانه برای هر شماره (Fire-and-Forget)
// این کار باعث میشود اگر یک شماره خطا داد، بقیه ارسال شوند
BackgroundJob.Enqueue(x => x.SendSingleSms(phone, message));
}
}
}
مرحله ۳: فراخوانی در کنترلر
حالا کافیست در API خود، متد ارسال گروهی را صدا بزنید:
[ApiController]
[Route("api/[controller]")]
public class NotificationController : ControllerBase
{
private readonly ISmsService _smsService;
public NotificationController(ISmsService smsService)
{
_smsService = smsService;
}
[HttpPost("send-marketing-sms")]
public IActionResult SendMarketingSms(string text)
{
// فرض کنید ۳۰۰۰ شماره را از دیتابیس میگیرید
var users = Enumerable.Range(1, 3000).Select(i => $"0912000{i:D4}").ToList();
// ثبت کردن کار در Hangfire و آزادسازی سریع درخواست کاربر
BackgroundJob.Enqueue(() => _smsService.SendBulkSms(users, text));
return Ok("عملیات ارسال در صف قرار گرفت. میتوانید وضعیت را در پنل مشاهده کنید.");
}
}
مقایسه عملکردی در مقیاس ۳۰۰۰ پیامک
|
ویژگی |
BackgroundService |
Quartz.NET |
Hangfire |
|
ذخیرهسازی |
فقط RAM (ناپایدار) |
دیتابیس / RAM |
دیتابیس (بسیار پایدار) |
|
پنل مانیتورینگ |
ندارد |
ندارد (نیاز به کتابخانه جانبی) |
دارد (Built-in) |
|
تلاش مجدد (Retry) |
باید دستی کدنویسی شود |
قابل تنظیم اما پیچیده |
خودکار و هوشمند |
|
سادگی استفاده |
بسیار ساده |
متوسط |
بسیار ساده |
|
هزینه سربار |
بسیار کم |
متوسط |
متوسط (به دلیل دیتابیس) |
تحلیل فنی: چرا BackgroundService برای ۳۰۰۰ پیامک خطرناک است؟
اگر از BackgroundService استفاده کنید، احتمالاً کدی شبیه به این خواهید داشت:
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
while (!stoppingToken.IsCancellationRequested)
{
var task = await _queue.DequeueAsync(stoppingToken);
await SendSms(task);
}
}
مشکل کجاست؟
اگر در حین ارسال پیامک ۱۵۰۰ام، اپلیکیشن شما به دلیل آپدیت شدن، کرش کردن یا کمبود RAM ریستارت شود، لیست ۱۵۰۰ شماره باقیمانده که در صف حافظه (Memory Queue) بودند برای همیشه پاک میشوند. اما در Hangfire، چون وضعیت هر Job در دیتابیس ذخیره شده، پس از بالا آمدن سیستم، Worker دوباره سراغ کارهای انجام نشده میرود.
نکات حرفهای برای ارسال انبوه
۱. Batching: به جای ایجاد ۳۰۰۰ جاب تکتک (که ممکن است دیتابیس Hangfire را شلوغ کند)، پیامکها را در دستههای ۱۰۰ تایی (Batch) تقسیم کنید.
۲. Rate Limiting: اکثر پنلهای پیامکی اجازه ارسال بیش از حد در ثانیه را نمیدهند. با استفاده از Thread.Sleep یا تنظیمات Worker در Hangfire، سرعت ارسال را کنترل کنید.
۳. Dashboard Security: حتماً روی پنل Hangfire احراز هویت بگذارید تا هر کسی نتواند جابها را حذف یا دوباره اجرا کند.
نتیجهگیری: کدام را انتخاب کنیم؟
-
اگر پروژه شما کوچک است و از دست رفتن دادهها در صورت ریستارت سرور اهمیتی ندارد، از BackgroundService استفاده کنید.
-
اگر نیاز به زمانبندیهای بسیار پیچیده تقویمی دارید، Quartz.NET انتخاب سنتی و قدرتمندی است.
-
اما برای سناریوهای عملیاتی حساس مثل ارسال ۳۰۰۰ پیامک، که "اطمینان از انجام" و "قابلیت پیگیری" حرف اول را میزند، بدون شک Hangfire بهترین گزینه است.
0 نظر
هنوز نظری برای این مقاله ثبت نشده است.