提供一個可排程、非同步的工作處理,此套件同時提供免費版本和付費版本,免費版本可商業使用,付費版本則是額外提供進階的功能,詳細可到 Hangfire 官網 查看
本篇不會介紹如何使用 Hangfire,而是會聚焦在於如何實作自訂工作重試處理。Hangfire 本身雖然已經提供工作重試處理,但是如果對於工作重試處理有特定的需求,就可以自己實作工作重試處理
如果符合以需求皆適用:
客製工作重試處理的實作會透過一個 Hangfire Job Filter 來處理,實作範例是一個讓服務忙碌重試和錯誤重試擁有各自獨立的重試計數器和重試間隔時間
建立先建立一個 自訂 Job Filter 參數,用來傳遞工作重試計數器和重試間隔時間
public class CustomAutoRetryOptions { // 發生錯誤時,最大重試次數 public int ErrorRetryAttempts { get; set; } // 錯誤的重試間隔秒數 public int ErrorRetryDelayInSeconds { get; set;} // 服務忙碌時,最大重試次數 public int BusyRetryAttempts { get; set; } // 服務忙碌的重試間隔秒數 Public int BusyRetryDelayInSeconds { get; set; } public CustomAutoRetryOptions() { } public CustomAutoRetryOptions(CustomAutoRetryOptions options) { ErrorRetryAttempts = options.ErrorRetryAttempts; ErrorRetryDelayInSeconds = options.ErrorRetryDelayInSeconds; BusyRetryAttempts = options.BusyRetryAttempts; BusyRetryDelayInSeconds = options.BusyRetryDelayInSeconds; } }
建立一個 Job Filter Attribute,繼承 JobFilterAttribute
和 IElectStateFilter
加入 Custom Job 參數
ErrorRetryAttempts
─ 錯誤重試ErrorRetryDelayInSeconds
─ 錯誤重試間隔BusyRetryAttempts
─ 服務忙碌重試BusyRetryDelayInSeconds
─ 服務忙碌重試間隔加入其他屬性
實作建構函式
實作 Custom Job Filter 的 OnStateElection
,詳細內容會在 Step.3 說明
// [1] 建立一個 Job Filter Attribute,繼承 JobFilterAttribute 和 IElectStateFilter public class CustomAutoRetryAttribute : JobFilterAttribute, IElectStateFilter { // [2] 加入 Custom Job 參數 public int ErrorRetryAttempts { get; set; } = 3; public int ErrorRetryDelayInSeconds { get; set; } = 5; public int BusyRetryAttempts { get; set; } = 3; public int BusyRetryDelayInSeconds { get; set; } = 5; // [3] 加入其他屬性 public bool LogEvents { get; set; } public AttemptsExceededAction OnAttemptsExceeded { get; set; } // [4] 建構函式 public CustomAutoRetryAttribute() { LogEvents = true; OnAttemptsExceeded = AttemptsExceededAction.Fail; Order = 20; } // [5] 實作 Custom Job Filter 的 OnStateElection public void OnStateElection(ElectStateContext context) { // 詳細內容會在 Step.3 說明 } }
覆寫 OnStateElection
可以依據 Exception 類型做對應的處理
取出 Custom Job Filter Options
取出的來源有兩個:
CustomAutoRetryAttribute
設定的值 (固定值)// [1] 覆寫 OnStateElection public void OnStateElection(ElectStateContext context) { // Job 執行結束,檢查 Job 執行狀態 if (context.CandidateState is FailedState && context.CandidateState != null) { // Job 執行失敗 (發生 Exception) 時的處理 var failedState = context.CandidateState as FailedState; // [3] 取出 Custom Job Filter Options var options = GetAutoRetryOptions(context); // [2] 依據 Exception 類型做對應的處理 if (failedState.Exception != null && failedState.Exception is BusyServiceException) { // 服務忙碌時 // TODO: do something } else { // 其他錯誤時 // TODO: do something } } } // [3] 試著從 Job Function Arguments 中取出 Custom Job Filter Options private CustomAutoRetryOptions GetAutoRetryOptions(ElectStateContext context) { // (動態) 從 Job Function Arguments 中取出 Custom Job Filter Options var args = context.BackgroundJob.Job.Args.ToList(); foreach (var arg in args) { if (arg.GetType() == typeof(CustomAutoRetryOptions)) { return new CustomAutoRetryOptions((CustomAutoRetryOptions)arg); } } // (固定) 取出 Custom Job Filter 的設定作為 Custom Job Filter Options return new CustomAutoRetryOptions() { ErrorRetryAttempts = ErrorRetryAttempts, ErrorRetryDelayInSeconds = ErrorRetryDelayInSeconds, BusyRetryAttempts = BusyRetryAttempts, BusyRetryDelayInSeconds = BusyRetryDelayInSeconds }; }
public class BusyServiceException : Exception { }
將 Custom Job Filter 加入到 Hangfire Filters
Startup.cs
using Hangfire; public class Startup { public void Configure(IApplicationBuilder app, IHostingEnvironment env) { // 將 Custom Job Filter 加入到 Hangfire Filters GlobalJobFilters.Filters.Add(new CustomAutoRetryAttribute()); } }
在 Job Method 前面加上 CustomAutoRetry
原生的 Auto Retry 處理必須關閉,不然 CustomAutoRetry
處理完後,緊接的執行原生的 Auto Retry
靜態指派最大重試次數、重試間隔時間
CustomAutoRetryAttribute
加入重試次數、重試間隔時間動態指派最大重試次數、重試間隔時間
可以透過 Throw Exception 來觸發 Job Retry
// 自訂 Retry 處理 (動態指派計數器、間隔時間) [CustomAutoRetry()] [AutomaticRetry(Attempts = 0)] // 關閉原生的 Retry 處理 public Task DoErrorThing(CustomAutoRetryOptions retryOptions) { // TODO: Do SomeThing // 透過 Throw Exception 來觸發 Job Retry throw new BusyServiceException(); } // 自訂 Retry 處理 (靜態指派計數器、間隔時間) [CustomAutoRetry(BusyRetryAttempts = 3, BusyRetryDelayInSeconds = 120)] [AutomaticRetry(Attempts = 0)] // 關閉原生的 Retry 處理 public Task DoBusyThing() { // TODO: Do SomeThing // 透過 Throw Exception 來觸發 Job Retry throw new BusyServiceException(); }
執行工作
CustomAutoRetryOptions
可以動態指派最大重試次數、重試間隔時間// 非同步處理 Request var retryOptions = new CustomAutoRetryOptions(); // 設定錯誤重試、服務忙碌重試的次數和間隔時間 int errorRetryAttempts = 3; int errorRetryDelayInSeconds = 10; int busyRetryAttempts = 5; int busyRetryDelayInSeconds = 60; retryOptions = retryOptions.SetErrorRetry(errorRetryAttempts, errorRetryDelayInSeconds) .SetBusyRetry(busyRetryAttempts, busyRetryDelayInSeconds); // 錯誤重試、服務忙碌重試的次數和間隔時間的設定需要傳入 (動態指派) BackgroundJob.Enqueue<DemoJob>(job => job.DoErrorThing(retryOptions)); // 錯誤重試、服務忙碌重試的次數和間隔時間的設定需要傳入 (靜態指派) BackgroundJob.Enqueue<DemoJob>(job => job.DoBusyThing());