Hangfire یک زمانبندی وظایف multi-threaded و مقیاس پذیر است که بر روی معماری Client-Server در پشتهی .NET باذخیرهسازی میانی در یک پایگاهداده ساخته شده، است.
زمانبندی وظایف
اگر قصد اجرای برخی کارها به صورت زمانبندی شده و در فواصل زمانی مشخصی را دارید، این مقاله به شما کمک خواهد کرد تا به بهترین شکل ممکن آن را انجام دهید.
کارهایی مانند ارسال خبرنامه، فرستادن SMS تبریک تولد یا هماهنگ سازی دادهها بین دو منبع داده از جمله اعمالی هستند که باید به صورت زمانبندی شده انجام شوند.
یک نسخهی رایگان LGPL v3 بصورت متنباز موجود است. در این مقاله، به بررسی و جستجوی نحوهی استفاده از Hangfire میپردازیم.
فهرست مطالب
- ویژگیهای منحصر بفرد عملکرد
- ویژگیهای سرور Hangfire
- کارهای تکراری و معوق
- مروری مختصر بر Quartz.NET
- Load testing
ویژگیهای منحصر بفرد عملکرد
همانطور که در تصویر مشاهده می کنید، سرویسگیرنده کاری را به یک پایگاهداده اضافه میکند؛ هنگامیکه یک سرور پایگاهداده را واکشی و کارها را در پشتزمینه اجرا میکند.
اطلاعات زیر را به خاطر داشته باشید:
سرویسگیرنده و سرور هر دو به پایگاهدادهی مشترک و اسمبلیهایی که کلاسهای کاری در آنها تعریف شدهاند، دسترسی دارند.
- مقیاسپذیری بار وجود دارد؛ میتوانید تعداد سرورها را افزایش دهید.
- Hangfire نمیتواند بدون پایگاهدادهها کار کند. بطور پیشفرض، از SQL Server پشتیبانی میکند، و افزونههایی برای DBMS مشهور وجود دارد. نسخهی تجاری آن از Redis پشتیبانی میکند.
- میتوانید از Hangfire در برنامههای ASP.NET، Windows Service ها، برنامههای کنسولی، و همینطور در Azure Worker Role استفاده کنید.
از دید سرویسگیرنده، کار ما یک وظیفه بر اساس قانون «fire-and-forget» است.
هیچ اتفاقی جز ذخیرهی وظیفه در یک پایگاهداده در سمت سرویسگیرنده رخ نمیدهد.
برای مثال، میخواهیم MethodToRun را در یک پردازش مجزا اجرا کنیم:
BackgroundJob.Enqueue(() => MethodToRun(42, "foo"));
Hangfire این کار را با مقادیر ورودی سریالسازی کرده و آن را در پایگاهداده ذخیره میکند:
{ "Type": "HangClient.BackgroundJobClient_Tests, HangClient, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null", "Method": "MethodToRun", "ParameterTypes": "(\"System.Int32, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089\",\"System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089\")", "Arguments": "(\"42\",\"\\\"foo\\\"\")" }
این اطلاعات برای فراخوانی متد MethodToRun در یک پردازش جداگانه از طریق reflection، به شرط داشتن دسترسی به اسمبلی Hangclient که در آن تعریف میشود، کافی میباشد.
قطعا نگهداری کد برای پردازش پشتزمینه در اسمبلی دارای سرویسگیرنده ضروری نمیباشد. نمودار وابستگی بصورت زیر است:
سرویسگیرنده و سرور باید به اسمبلی مشترک دسترسی داشته باشند، در حالیکه رابط تعبیه شده به آن نیازی ندارد.
امکان جایگزینی پیادهسازی کار ذخیره شده از طریق جایگزینی اسمبلی، که برنامهی سرور به آن reference دارد، وجود دارد.
اگر MethodToRun هم با اسمبلیهای قدیمی و هم جدید منطبق باشد، برای کارهای تکراری مناسب است.
تنها محدودیت، داشتن یک تصحیحکنندهی عمومی است.
اگر نیاز دارید یک شیء را ایجاد و متد آن را فراخوانی کنید، Hangfire این کار را برای شما انجام میدهد:
BackgroundJob.Enqueue<EmailSender>(x => x.Send(13, "Hello!"));
حتی میتواند نمونهی EmailSender را با استفاده از یک DI-container بازیابی کند.
همچنین، استقرار سرور را، برای مثال در یک Windows Service مجزا، سادهسازی میکند:
public partial class Service1 : ServiceBase { private BackgroundJobServer _server; public Service1() { InitializeComponent(); GlobalConfiguration.Configuration.UseSqlServerStorage("connection_string"); } protected override void OnStart(string() args) { _server = new BackgroundJobServer(); } protected override void OnStop() { _server.Dispose(); } }
پس از آغاز به کار سرویس، سرور Hangfire شروع به بازیابی کارها از پایگاهداده و اجرای آنها میکند.
جهت مدیریت پردازش کار، میتوانید از داشبورد وب تعبیه شده استفاده کنید:
ویژگیهای سرور Hangfire
سرور حاوی مخزن thread pool خود است که با استفاده از Task Parallel Library بر پایهی Task.WaitAll پیادهسازی شدهاست.
جهت اطلاعات بیشتر، به BackgroundProcessingServer مراجعه کنید.
از Web Frame و Web Garden پشتیبانی میکند:
میتوانیم هر تعداد سرور Hangfire بدون نگرانی در رابطه با همگامسازی آنها ایجاد کنیم.
Hangfire اطمینان حاصل میکند که یک سرور تنها یک کار را اجرا کند. میتوانید با استفاده از sp-getapplock آن را بررسی کنید (به کلاس SqlServerDistributedLock رجوع کنید).
همانطور که بحث شد، سرور Hangfire نیاز به میزبان خاصی ندارد و میتواند از Console App به Azure Web Site استقرار یابد.
هنگام میزبانی در ASP.NET، نیاز است چنین ویژگیهای کلی از IIS مانند بازیافت پردازش، شروع خودکار (startMode=«AlwaysRunning») و … را در نظر بگیرید.
با این حال، Document یک زمانبندی در این حالت اطلاعات معناداری را ارائه میدهد.
بعلاوه، کیفیت مستندات عالی است. source code Hangfire عمومی و با کیفیت بالا است، همچنین هیچ مرزی برای استقرار یک سرور محلی و اشکالزدایی یک کد وجود ندارد.
کارهای تکراری و معوق
Hangfire کاربران را قادر میسازد تا کارهای تکراری را با حداقل فاصلهی زمانی در دقیقه ایجاد کنند:
RecurringJob.AddOrUpdate(() => MethodToRun(42, "foo"), Cron.Minutely);
امکان اجرای کار بصورت دستی یا حذف آن وجود دارد:
RecurringJob.Trigger("task-id"); RecurringJob.RemoveIfExists("task-id");
علاوه بر آن، میتوانید اجرای کار را معوق سازید:
BackgroundJob.Schedule(() => MethodToRun(42, "foo"), TimeSpan.FromDays(7));
برای انجام این کار، از عبارات CRON استفاده کنید. برای مثال، کار زیر هر روز در ساعت ۲:۱۵ قبل از ظهر انجام خواهد شد:
RecurringJob.AddOrUpdate("task-id", () => MethodToRun(42, "foo"), "15 2 * * *");
مروری مختصر بر Quartz.NET
به عنوان جایگزینی برای Hangfire، در فریمورک .NET، میتوانیم از Quartz.NET، درگاهی از زمانبندی وظایف Java – Quartz، استفاده کنیم.
Quartz.NET کاری مشابه را حل کرده و نیز هر تعداد سرویسگیرنده و سرور که از پایگاهدادهای مشترک استفاده میکنند را پشتیبانی میکند. با این حال، در فرآیند اجرایشان متفاوت هستند.
کد GitHub تا زمانیکه بطور دستی ؛ چندین references ، فایل و اسمبلی ناموجود را درست نکردم، کامپایل نشده بود.
هیچ نوع تفکیک به بخش سرور و سرویسگیرندهای در پروژه وجود ندارد.
Quartz.NET به عنوان یک DDL مجزا اعمال میشود. بنابراین، اگر نمونهای مشخص برای افزودن کارها، در عوض اجرای آنها، میخواهید، نیاز است آن نمونه را راهاندازی کنید.
Quartz.Net رایگان بوده و شما را قادر میسازد تا کارها را با استفاده از پایگاهدادههای درون حافظهای و معروف (SQLServer، Oracle، MySQL، SQLite، …) ذخیره کنید.
انبار درون حافظهای، دادهها را در حافظهی پردازش سروری که کارها را انجام میدهد نگه میدارد.
امکان پیادهسازی چندین پردازش سروری تنها هنگام ذخیرهی کارها در پایگاهداده وجود دارد.
برای همگامسازی، Quartz.NET از یک الگوریتم رایج استفاده میکند.
برای مثال، با ثبتنام در جدول QRTZ_LOCKS میتوانید همزمان تنها یک پردازش زمانبندی را با یک ID منحصر بفرد اجرا کنید.
یک تغییر وضعیت ساده در جدول QRTZ_TRIGGERS کار را جهت اجرا بازگردانی خواهد کرد.
کلاس Job در Quartz.NET باید رابط IJob را پیادهسازی کند:
public interface IJob { void Execute(IJobExecutionContext context); }
با محدودیتی مشابه، سریالی کردن کار آسان است: یک پایگاهداده یک نام کامل کلاس را ذخیره میکند، که برای گرفتن نوع کلاس job با استفاده از Type.GetType name کافی میباشد.
کلاس JobDataMap امکان ارسال پارامترها در کار (job) را فراهم میکند. علاوه بر آن، میتوانید پارامترهای کار ذخیره شده را تغییر دهید.
برای موضوع Quartz.NET، multithreading از کلاسهای System.Threading استفاده میکند:
()new Thread ( مراجعه کنید به custom thread pools ( QuartzThread ، و synchronization،
مستندات رسمی، اطلاعات زیادی در این موضوع ارائه نمیدهند. نگاهی به این بخش در رابطه با clustering بیندازید:
در سایت StackOverflow، یکی از کاربران راهنماهای Quartz را پیشنهاد داده است.
از آنجایی که توسعهدهندگان Java و .NET از API مشابهی پشتیبانی نمیکنند، Quartz.NET نسخههای منتشر شده و بروزرسانیهای زیادی ندارد.
مثالی از API سرویسگیرنده: ثبت کار تکراری HelloJob
IScheduler scheduler = GetSqlServerScheduler(); scheduler.Start(); IJobDetail job = JobBuilder.Create<HelloJob>() .Build(); ITrigger trigger = TriggerBuilder.Create() .StartNow() .WithSimpleSchedule(x => x .WithIntervalInSeconds(10) .RepeatForever()) .Build(); scheduler.ScheduleJob(job, trigger);
میتوانید ویژگیهای اصلی زمانبندی وظایف را در جدول زیر بیابید:
Load testing
برای بررسی اینکه Hangfire چگونه از پس تعداد بسیار زیادی کار برمیآید، یک سرویسگیرندهی ساده که هر ۰.۲ ثانیه کاری را ایجاد میکند، ایجاد کردهایم.
هر کار، سطری با دادههای اشکالزدایی در یک پایگاهداده مینویسد. با محدود کردن یک سرویسگیرنده به ۱۰۰K کار، دو نمونه سرویسگیرنده و یک سرور با dotMemory پیادهسازی کردیم.
شش ساعت بعد، ۲۰۰K کار موفقیت آمیز در Hangfire و ۲۰۰K سطر افزوده شده در پایگاهداده بدست آوردم.
تصویر زیر نتایج سرویس profiling را نشان میدهد.
دو تصویر از حافظه: قبل و بعد از اجرای آنها:
در مراحل بعدی، ۲۰ پردازش سرویسگیرنده و ۲۰ پردازش سرور از قبل وجود داشتند. زمان اجرای کار کاهش یافت و تبدیل به مقداری تصادفی شد. با این حال، این پردازش ها تأثیری بر روی Hangfire نگذاشتند:
نتیجهگیری زمانبندی وظایف:
Hangfire بهتر است.زیرا یک سرویس رایگان و عمومی است، که هزینههای توسعه و نگهداری سیستمهای توزیع شده را کاهش میدهد.
هیچ دیدگاهی نوشته نشده است.