Hangfire कतार समर्थन

हैंगफ़ायर .net (कोर) के लिए एक पुस्तकालय है, जो "आग और भूल" के सिद्धांत पर कुछ कोड के अतुल्यकालिक निष्पादन की अनुमति देता है। इस तरह के कोड का एक उदाहरण ई-मेल, वीडियो प्रसंस्करण, किसी अन्य सिस्टम के साथ सिंक्रनाइज़ेशन आदि भेज सकता है। "आग और भूल" के अलावा आस्थगित कार्यों के लिए समर्थन है, साथ ही क्रोन प्रारूप में अनुसूचित कार्य भी हैं।


वर्तमान में, ऐसे कई पुस्तकालय हैं। Hangfire के कुछ लाभ हैं:


  • सरल विन्यास, सुविधाजनक एपीआई
  • विश्वसनीयता। Hangfire गारंटी देता है कि बनाए गए कार्य को कम से कम एक बार निष्पादित किया जाएगा
  • समानांतर और उत्कृष्ट प्रदर्शन में कार्य करने की क्षमता
  • एक्स्टेंसिबिलिटी (यहाँ हम इसे नीचे इस्तेमाल करेंगे)
  • काफी हद तक पूर्ण और समझने योग्य दस्तावेज
  • डैशबोर्ड जिस पर आप कार्यों के बारे में सभी आँकड़े देख सकते हैं

मैं बहुत अधिक विस्तार में नहीं जाऊंगा, क्योंकि हैंगफायर के बारे में कई अच्छे लेख हैं और इसका उपयोग कैसे करना है। इस लेख में मैं चर्चा करूँगा कि कई कतारों (या टास्क पूल) के समर्थन का उपयोग कैसे किया जाए, मानक रीट्री-फ़ंक्शनलिटी को कैसे ठीक किया जाए और प्रत्येक कतार को एक अलग कॉन्फ़िगरेशन दिया जाए।


(छद्म) कतारों के लिए मौजूदा समर्थन


महत्वपूर्ण नोट: शीर्षक में, मैंने छद्म-कतार शब्द का उपयोग किया है क्योंकि हैंगफ़ायर गारंटी नहीं देता है कि कार्यों को एक विशिष्ट क्रम में किया जाएगा। यानी "फर्स्ट इन फर्स्ट आउट" का सिद्धांत लागू नहीं होता है और हम इस पर भरोसा नहीं करेंगे। इसके अलावा, पुस्तकालय के लेखक कार्यों को बेकार बनाने की सलाह देते हैं, अर्थात्। अप्रत्याशित कई निष्पादन के खिलाफ स्थिर। इसके अलावा मैं सिर्फ "कतार" शब्द का उपयोग करूंगा, क्योंकि हैंगफ़ायर शब्द "कतार" का उपयोग करता है।


Hangfire में सरल कतार समर्थन है। यद्यपि यह संदेश कतार प्रणालियों जैसे लचीलेपन की पेशकश नहीं करता है जैसे कि खरगोशबेक्यू या एज़्योर सर्विस बस, यह अक्सर कार्यों की एक विस्तृत श्रृंखला को हल करने के लिए पर्याप्त है।


प्रत्येक कार्य में "कतार" संपत्ति है, अर्थात, कतार का नाम जिसमें इसे निष्पादित किया जाना चाहिए। डिफ़ॉल्ट रूप से, कार्य को "डिफ़ॉल्ट" नाम के साथ कतार में भेजा जाता है जब तक कि अन्यथा निर्दिष्ट न हो। विभिन्न प्रकार के कार्यों के निष्पादन को अलग से प्रबंधित करने के लिए कई कतारों के लिए समर्थन की आवश्यकता होती है। उदाहरण के लिए, हम चाहते हैं कि वीडियो प्रसंस्करण कार्य "video_queue" कतार में पड़ें, और "email_queue" कतार में ई-मेल भेजें। इस प्रकार, हम इन दो प्रकार के कार्यों को स्वतंत्र रूप से करने में सक्षम हैं। अगर हम वीडियो प्रोसेसिंग को एक समर्पित सर्वर पर ले जाना चाहते हैं, तो हम एक अलग हैंगफायर सर्वर को कंसोल एप्लिकेशन के रूप में चलाकर आसानी से कर सकते हैं जो "वीडियो_क्यू" कतार को संसाधित करेगा।


अभ्यास के लिए आगे बढ़ते हैं


Asp.net कोर में Hangfire सर्वर की स्थापना इस प्रकार है:


public void Configure(IApplicationBuilder app) { app.UseHangfireServer(new BackgroundJobServerOptions { WorkerCount = 2, Queues = new[] { "email_queue", "video_queue" } }); } 

समस्या 1 - रिप्ले कार्य डिफ़ॉल्ट कतार में आते हैं


जैसा कि मैंने ऊपर कहा है, "डिफ़ॉल्ट" नामक Hangfire में एक डिफ़ॉल्ट कतार है। यदि कतार में रखा गया कार्य, उदाहरण के लिए, "video_queue", विफल हो गया और उसे वापस लेने की आवश्यकता है, तो उसे फिर से "डिफ़ॉल्ट" कतार में भेजा जाएगा और "video_queue" नहीं और, परिणामस्वरूप, हमारा कार्य बिल्कुल भी नहीं किया जाएगा हैंगफायर सर्वर का उदाहरण जो हम चाहते हैं, यदि है। यह व्यवहार मेरे द्वारा प्रयोगात्मक रूप से स्थापित किया गया था और संभवतः हैंगफायर में ही एक बग है।


जॉब फिल्टर


Hangfire हमें तथाकथित फ़िल्टर ( जॉब फिल्टर ) की मदद से कार्यक्षमता का विस्तार करने की संभावना प्रदान करता है, जो कि ASP.NET MVC में एक्शन फिल्टर्स के सिद्धांत के समान हैं। तथ्य यह है कि हैंगफायर के आंतरिक तर्क को स्टेट मशीन के रूप में लागू किया गया है। यह एक इंजन है जो क्रमिक रूप से पूल में कार्यों को एक राज्य से दूसरे राज्य में स्थानांतरित करता है (उदाहरण के लिए, बनाया गया -> संलग्न -> प्रसंस्करण -> सफल), और फिल्टर हमें हर बार अपने राज्य परिवर्तन को निष्पादित करने और इसे हेरफेर करने के लिए कार्य को "अवरोधन" करने की अनुमति देते हैं। एक फ़िल्टर को एक विशेषता के रूप में लागू किया जाता है जिसे एकल विधि, वर्ग या वैश्विक स्तर पर लागू किया जा सकता है।


नौकरी के मापदंडों


ElectStateContext ऑब्जेक्ट को फ़िल्टर विधि के तर्क के रूप में पास किया जाता है। इस ऑब्जेक्ट में वर्तमान कार्य के बारे में पूरी जानकारी है। अन्य बातों के अलावा, इसमें GetJobParameter <> (...) और SettJobParameter <> (...) विधियाँ हैं। नौकरी पैरामीटर आपको किसी डेटाबेस में किसी कार्य से संबंधित जानकारी को सहेजने की अनुमति देता है। यह जॉब पैरामीटर्स में है कि जिस कतार में कार्य को मूल रूप से भेजा गया था, उसका नाम संग्रहीत है, केवल कुछ कारणों से इस जानकारी को अगले रिट्री के दौरान अनदेखा किया जाता है।


निर्णय


इसलिए, हमारे पास एक कार्य है जो गलती से समाप्त हो गया है और इसे सही कतार में पुन: निष्पादन के लिए भेजा जाना चाहिए (उसी में जिसे प्रारंभिक निर्माण के समय इसे सौंपा गया था)। किसी कार्य की पुनरावृत्ति जो एक त्रुटि के साथ पूरी होती है, वह "विफल" राज्य से "एन्केच्ड" स्थिति में परिवर्तन है। समस्या को हल करने के लिए, एक फ़िल्टर बनाएं, जो कार्य "एन्केड" स्थिति में प्रवेश करता है, यह जाँच करेगा कि किस कतार में कार्य शुरू में भेजा गया था और वांछित मूल्य में "कतार नाम" पैरामीटर डाल दिया:


 public class HangfireUseCorrectQueueFilter : JobFilterAttribute, IElectStateFilter { public void OnStateElection(ElectStateContext context) { if (context.CandidateState is EnqueuedState enqueuedState) { var queueName = context.GetJobParameter<string>("QueueName"); if (string.IsNullOrWhiteSpace(queueName)) { context.SetJobParameter("QueueName", enqueuedState.Queue); } else { enqueuedState.Queue = queueName; } } } } 

सभी कार्यों के लिए डिफ़ॉल्ट फ़िल्टर लागू करने के लिए (अर्थात विश्व स्तर पर), हमारे कॉन्फ़िगरेशन में निम्न कोड जोड़ें:


 GlobalJobFilters.Filters.Add(new HangfireUseCorrectQueueFilter { Order = 1 }); 

एक अन्य छोटी पकड़ यह है कि GlobalJobFilters संग्रह डिफ़ॉल्ट रूप से AutomaticRetryAttribute वर्ग का एक उदाहरण है। यह एक मानक फ़िल्टर है जो विफल कार्यों को फिर से निष्पादित करने के लिए जिम्मेदार है। वह मूल कतार की अनदेखी करते हुए कार्य को "डिफ़ॉल्ट" कतार में भेजता है। हमारी बाइक की सवारी करने के लिए, आपको इस फ़िल्टर को संग्रह से निकालने की आवश्यकता है और हमारे फ़िल्टर को दोहराए गए कार्यों की जिम्मेदारी लेने दें। नतीजतन, कॉन्फ़िगरेशन कोड इस तरह दिखाई देगा:


 var defaultRetryFilter = GlobalJobFilters.Filters .FirstOrDefault(f => f.Instance is AutomaticRetryAttribute); if (defaultRetryFilter != null && defaultRetryFilter.Instance != null) { GlobalJobFilters.Filters.Remove(defaultRetryFilter.Instance); } GlobalJobFilters.Filters.Add(new HangfireUseCorrectQueueFilter { Order = 1 }); 

यह ध्यान दिया जाना चाहिए कि AutomaticRetryAttribute स्वचालित रूप से प्रयासों के बीच अंतराल को बढ़ाने के तर्क को लागू करता है (प्रत्येक बाद के प्रयास के साथ अंतराल बढ़ता है), और GlobalJobFilters संग्रह से AutomaticRetryAttribute को हटाते हुए, हम इस कार्यक्षमता को देखते हैं ( ScheduleAgainLater विधि का कार्यान्वयन देखें)


इसलिए, हमने यह हासिल किया है कि हमारे कार्य अलग-अलग कतारों में किए जा सकते हैं, और यह हमें अलग-अलग मशीनों पर अलग-अलग कतारों को संसाधित करने सहित स्वतंत्र रूप से उनके निष्पादन का प्रबंधन करने की अनुमति देता है। केवल अब हम नहीं जानते कि कितनी बार और किस अंतराल पर हमारे कार्यों को एक त्रुटि के मामले में दोहराया जाएगा, क्योंकि हमने फ़िल्टर संग्रह से AutomaticRetryAttribute को हटा दिया था।


समस्या 2 - प्रत्येक कतार के लिए अलग-अलग सेटिंग्स


हम प्रत्येक कतार के लिए अंतराल और पुनरावृत्ति की संख्या को अलग-अलग कॉन्फ़िगर करने में सक्षम होना चाहते हैं, और यह भी, अगर कुछ कतार के लिए हमने मूल्यों को स्पष्ट रूप से निर्दिष्ट नहीं किया है, तो हम चाहते हैं कि डिफ़ॉल्ट मान लागू किया जाए। ऐसा करने के लिए, हम एक और फ़िल्टर लागू करते हैं और इसे HangfireRetryJobFilter कहते हैं।


आदर्श रूप से, कॉन्फ़िगरेशन कोड कुछ इस तरह दिखना चाहिए:


 GlobalJobFilters.Filters.Add(new HangfireRetryJobFilter { Order = 2, ["email_queue"] = new HangfireQueueSettings { DelayInSeconds = 120, RetryAttempts = 3 }, ["video_queue"] = new HangfireQueueSettings { DelayInSeconds = 60, RetryAttempts = 5 } }); 

निर्णय


ऐसा करने के लिए, पहले HangfireQueueSettings वर्ग जोड़ें, जो हमारी सेटिंग्स के लिए एक कंटेनर के रूप में काम करेगा।


 public sealed class HangfireQueueSettings { public int RetryAttempts { get; set; } public int DelayInSeconds { get; set; } } 

फिर हम फ़िल्टर के कार्यान्वयन को स्वयं जोड़ते हैं, जो, जब एक त्रुटि के बाद कार्यों को दोहराया जाता है, तो कतार के कॉन्फ़िगरेशन के आधार पर सेटिंग्स को लागू करेगा और रिट्रीट की संख्या की निगरानी करेगा:


 public class HangfireRetryJobFilter : JobFilterAttribute, IElectStateFilter, IApplyStateFilter { private readonly HangfireQueueSettings _defaultQueueSettings = new HangfireQueueSettings { RetryAttempts = 3, DelayInSeconds = 10 }; private readonly IDictionary<string, HangfireQueueSettings> _settings = new Dictionary<string, HangfireQueueSettings>(); public HangfireQueueSettings this[string queueName] { get { return _settings.TryGetValue(queueName, out HangfireQueueSettings queueSettings) ? queueSettings : _defaultQueueSettings; } set { _settings[queueName] = value; } } public void OnStateElection(ElectStateContext context) { if (!(context.CandidateState is FailedState failedState)) { // This filter accepts only failed job state. return; } var retryAttempt = context.GetJobParameter<int>("RetryCount") + 1; var queueName = context.GetJobParameter<string>("QueueName"); if (retryAttempt <= this[queueName].RetryAttempts) { ScheduleAgainLater(context, retryAttempt, failedState, queueName); } else { TransitionToDeleted(context, failedState, queueName); } } public void OnStateApplied( ApplyStateContext context, IWriteOnlyTransaction transaction) { if (context.NewState is ScheduledState && context.NewState.Reason != null && context.NewState.Reason.StartsWith("Retry attempt")) { transaction.AddToSet("retries", context.BackgroundJob.Id); } } public void OnStateUnapplied( ApplyStateContext context, IWriteOnlyTransaction transaction) { if (context.OldStateName == ScheduledState.StateName) { transaction.RemoveFromSet("retries", context.BackgroundJob.Id); } } private void ScheduleAgainLater( ElectStateContext context, int retryAttempt, FailedState failedState, string queueName) { context.SetJobParameter("RetryCount", retryAttempt); var delay = TimeSpan.FromSeconds(this[queueName].DelayInSeconds); const int maxMessageLength = 50; var exceptionMessage = failedState.Exception.Message.Length > maxMessageLength ? failedState.Exception.Message.Substring(0, maxMessageLength - 1) + "…" : failedState.Exception.Message; // If attempt number is less than max attempts, we should // schedule the job to run again later. var reason = $"Retry attempt {retryAttempt} of {this[queueName].RetryAttempts}: {exceptionMessage}"; context.CandidateState = delay == TimeSpan.Zero ? (IState)new EnqueuedState { Reason = reason } : new ScheduledState(delay) { Reason = reason }; } private void TransitionToDeleted( ElectStateContext context, FailedState failedState, string queueName) { context.CandidateState = new DeletedState { Reason = this[queueName].RetryAttempts > 0 ? "Exceeded the maximum number of retry attempts." : "Retries were disabled for this job." }; } } 

कोड पर ध्यान दें: HangfireRetryJobFilter वर्ग को लागू करते समय, HangfireRetryJobFilter से AutomaticRetryAttribute वर्ग को एक आधार के रूप में लिया गया था, इसलिए कुछ तरीकों का कार्यान्वयन आंशिक रूप से इस वर्ग के संबंधित तरीकों के साथ मेल खाता है।

समस्या 3 - किसी कार्य को एक विशिष्ट कतार में कैसे भेजें?


मैं कतार में कार्य को असाइन करने के दो तरीके खोजने में कामयाब रहा: दस्तावेज और - नहीं।


पहली विधि - विधि पर संबंधित विशेषता लटकाएं


 [Queue("video_queue")] public void SomeMethod() { } BackgroundJob.Enqueue(() => SomeMethod()); 

http://docs.hangfire.io/en/latest/background-processing/configuring-queues.html


दूसरी विधि (अनिर्धारित) - BackgroundJobClient वर्ग का उपयोग करें


 var client = new BackgroundJobClient(); client.Create(() => MyMethod(), new EnqueuedState("video_queue")); 

दूसरी विधि का लाभ यह है कि यह Hangfire पर अनावश्यक निर्भरता नहीं बनाता है और आपको यह तय करने की अनुमति देता है कि किस प्रक्रिया के दौरान कार्य को जाना चाहिए। दुर्भाग्य से, आधिकारिक दस्तावेज में, मुझे BackgroundJobClient क्लास का उल्लेख नहीं मिला और इसे कैसे लागू किया जाए। मैंने अपने समाधान में दूसरी विधि का उपयोग किया, इसलिए यह व्यवहार में परीक्षण किया जाता है।


निष्कर्ष


इस लेख में, हमने विभिन्न प्रकार के कार्यों के प्रसंस्करण को अलग करने के लिए Hangfire में कई कतारों के समर्थन का उपयोग किया। हमने प्रत्येक पंक्ति के लिए व्यक्तिगत कॉन्फ़िगरेशन की संभावना के साथ असफल कार्यों को दोहराने के लिए अपने तंत्र को लागू किया, नौकरी फ़िल्टर का उपयोग करके Hangfire की कार्यक्षमता का विस्तार किया, और यह भी सीखा कि कार्यों को निष्पादन के लिए वांछित कतार में कैसे भेजा जाए।


मुझे उम्मीद है कि यह लेख किसी के लिए उपयोगी है। मुझे टिप्पणी करने में खुशी होगी।


उपयोगी लिंक


Hangfire प्रलेखन
Hangfire स्रोत कोड
स्कॉट हैंसेलमैन - ASP.NET में बैकग्राउंड टास्क कैसे चलाएं

Source: https://habr.com/ru/post/hi434364/


All Articles