選單
GSS 技術部落格
在這個園地裡我們將從技術、專案管理、客戶對談面和大家分享我們多年的經驗,希望大家不管是喜歡或是有意見,都可以回饋給我們,讓我們有機會和大家對話並一起成長!
若有任何問題請來信:gss_crm@gss.com.tw
7 分鐘閱讀時間 (1368 個字)

如何在 Hangfire上實作自訂工作重試處理

mockuuups-macbook-pro-mockup-on-a-womans-legs

Hangfire 簡介

  提供一個可排程、非同步的工作處理,此套件同時提供免費版本和付費版本,免費版本可商業使用,付費版本則是額外提供進階的功能,詳細可到 Hangfire 官網 查看


前言

  本篇不會介紹如何使用 Hangfire,而是會聚焦在於如何實作自訂工作重試處理。Hangfire 本身雖然已經提供工作重試處理,但是如果對於工作重試處理有特定的需求,就可以自己實作工作重試處理


適用情境

如果符合以需求皆適用:

  1. 因為不同的 Exception 而需要有各自的重試計數器
  2. 因為排程工作的不同而需要有各自不同的重試次數,且重試次數是動態值
  3. 因為排程工作的不同而需要有各自不同的重試的間隔時間,且重試間隔時間是動態值

實作

  客製工作重試處理的實作會透過一個 Hangfire Job Filter 來處理,實作範例是一個讓服務忙碌重試和錯誤重試擁有各自獨立的重試計數器和重試間隔時間

Step.1 建立 Custom Job Filter Options

建立先建立一個 自訂 Job Filter 參數,用來傳遞工作重試計數器和重試間隔時間

  • CustomAutoRetryOptions.cs
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;
    }
} 

Step.2 建立一個 Custom Job Filter

  1. 建立一個 Job Filter Attribute,繼承 JobFilterAttributeIElectStateFilter

  2. 加入 Custom Job 參數

    • ErrorRetryAttempts ─ 錯誤重試
    • ErrorRetryDelayInSeconds ─ 錯誤重試間隔
    • BusyRetryAttempts ─ 服務忙碌重試
    • BusyRetryDelayInSeconds ─ 服務忙碌重試間隔
  3. 加入其他屬性

    • 後面步驟會詳細說明
  4. 實作建構函式

  5. 實作 Custom Job Filter 的 OnStateElection,詳細內容會在 Step.3 說明

  • CustomAutoRetryAttribute.cs
// [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 說明
    }
} 

Step.3 實作 Custom Job Filter 的 OnStateElection

  1. 覆寫 OnStateElection

    • 不論 Job 成功與否,在 Job 執行後會進到這個 Method
  2. 可以依據 Exception 類型做對應的處理

  3. 取出 Custom Job Filter Options

    • 取出的來源有兩個:

      1. 來自 CustomAutoRetryAttribute 設定的值 (固定值)
      2. 來自 Job Function Arguments (動態值)
  • CustomAutoRetryAttribute.cs
// [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
    };
} 
  • BusyServiceException
public class BusyServiceException : Exception 
{
} 

Step.4 加入 Custom Job Filter

  • 將 Custom Job Filter 加入到 Hangfire Filters

    • 這樣的寫法屬於全域的 Filter
  • Startup.cs

using Hangfire;

public class Startup 
{
     public void Configure(IApplicationBuilder app, IHostingEnvironment env) 
    {
        // 將 Custom Job Filter 加入到 Hangfire Filters
        GlobalJobFilters.Filters.Add(new CustomAutoRetryAttribute());
        
    }
} 

Step.5 使用 Custom Auto Retry

  • 在 Job Method 前面加上 CustomAutoRetry

    • 原生的 Auto Retry 處理必須關閉,不然 CustomAutoRetry 處理完後,緊接的執行原生的 Auto Retry

    • 靜態指派最大重試次數、重試間隔時間

      • 直接在 CustomAutoRetryAttribute 加入重試次數、重試間隔時間
    • 動態指派最大重試次數、重試間隔時間

      • 需要先建立 Custom Job Filter Options,並透過 Method Arguments 將重試次數、重試間隔時間傳入
  • 可以透過 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()); 
使用 OPENJSON 及 FOR JSON 剖析及轉換 JSON 資料 (MS SQL)
[.Net core] HttpMessageResponse Download File not ...

相關文章

 

評論

尚無評論
已經注冊了? 這裡登入
Guest
2024/04/30, 週二

Captcha 圖像