前面幾篇有提到 Job Enqueue Retry,對他的理解好像不夠,今天花一點時間研究研究,然後一整天就不見了...
開發環境
VS 2019
NET Framework 4.8
開啟一個空白的 Web Application 專案
安裝以下套件
Install-Package Microsoft.AspNet.WebApi.OwinSelfHost
Install-Package Microsoft.Owin.Diagnostics
Install-Package Microsoft.Owin.Host.SystemWeb
Install-Package Hangfire
Install-Package Hangfire.Console
Install-Package Hangfire.MemoryStorage
Install-Package Swagger-Net
除了,SwaggerConfig.Register 記得要改成用 OWIN 的 HttpConfiguration,其他的,設定可以參考 https://dotblogs.com.tw/yc421206/2019/12/25/desktop_app_async_task_via_hangfire
實作
替每一個 Job,對應一個 Swagger API,讓我可以模擬參數的傳入,然後觀察 Hangfire 的變化
Swagger UI 位置:/swagger
Hangfire Dashboard 位置:/hangfire
等待逾時
DisableConcurrentExecutionAttribute
timeoutInSeconds = 5,等五秒鐘還沒有執行就是 TimeOut,這個任務會被判定錯誤
[JobDisplayName("WaitTimeout - {0}")]
[DisableConcurrentExecution(5)]
public void WaitTimeout(string msg, PerformContext context, IJobCancellationToken cancelToken)
{
if (msg == "1")
{
context.WriteLine($"執行中,目前時間:{DateTime.Now}");
Thread.Sleep(60000);
}
context.WriteLine($"執行完畢:目前時間{DateTime.Now}");
}
演練步驟,先傳 "1" 給 WaitTimeout 方法,再傳 "2" 給WaitTimeout 方法,WaitTimeout - 2 的 Queue 已經被移到 Failded,WaitTimeout - 1 還在 Retry,如下圖:
執行逾時
MemoryStorageOptions
MemoryStorageOptions.FetchNextJobTimeout = new TimeSpan(0, 0, 5),等待 5 秒,還沒有完成,這個任務會被移到 Failed
GlobalConfiguration.Configuration
.UseMemoryStorage(new MemoryStorageOptions
{
FetchNextJobTimeout = new TimeSpan(0, 0, 5)
})
.UseConsole()
;
這裡為了演示所以使用 MemoryStorageOptions,線上環境你可以換成持久化的設定
重試時重新排隊
AutomaticRetryAttribute
當例外發生時會觸發自動重試,比如網路不穩定,導致訪問不到遠端服務,這時可以使用 AutomaticRetryAttribute
重試時,工作會離開當前 Queue (佇列),重新排隊,不會堵塞整個 Queue,讓下一個工作可以運作。
兩種用法,一種是掛在 Job Method 上面,一種是註冊 Filter,GlobalJobFilters.Filters.Add(new AutomaticRetryAttribute { Attempts = 3 }),套用所有服務,兩者可以混用,當有衝突時以 Job Method 為主
- Attempts:重試次數, 0 為不重試。
- DelaysInSeconds:延遲時間,指定重試需要延遲多久才執行。
- OnAttemptsExceeded:超過重試次數要歸類到 Delete、Fail 狀態
[AutomaticRetry(Attempts = 3, DelaysInSeconds = new[] {10, 20, 30},OnAttemptsExceeded = AttemptsExceededAction.Delete)]
public void Retry(string msg, PerformContext context, IJobCancellationToken cancelToken)
{
//TODO....
}
重試超過次數後要歸類的狀態,如下圖:
代碼如下:
[JobDisplayName("AutoRetry - {0}")]
[AutomaticRetry(Attempts = 3,
DelaysInSeconds = new[] {5, 10, 15},
OnAttemptsExceeded = AttemptsExceededAction.Delete)]
[DisableConcurrentExecution(30)]
public void AutoRetry(string msg, PerformContext consoleLog, IJobCancellationToken cancelToken)
{
if (msg == "1")
{
consoleLog.WriteLine($"執行中,目前時間:{DateTime.Now}");
Thread.Sleep(5000);
throw new Exception("噴錯了~");
}
consoleLog.WriteLine($"執行完畢,目前時間:{DateTime.Now}");
}
演練步驟,先傳 "1" 給AutoRetry 方法,再傳 "2",AutoRetry - 1 的 Queue 發生錯誤重試,重新排隊,AutoRetry - 2 已經成功,如下圖:
重試時不重新排隊
目前我找不到 Hangfire 的相關設定可以滿足這個情境,我想要借助 Polly 來幫我在原本的 Queue 重試,不進入排隊流程
[HttpGet]
[Route("PollyRetry")]
public void PollyRetry(string msg)
{
this._client.Enqueue(() => this._job.PollyRetry(msg, null, null));
}
Job Client 呼叫 Polly,Retry 的工作交給 Polly,Polly 也有延遲重試
[JobDisplayName("PollyRetry - {0}")]
[AutomaticRetry(Attempts = 0, OnAttemptsExceeded = AttemptsExceededAction.Delete)]
[DisableConcurrentExecution(120)]
public void PollyRetry(string msg, PerformContext consoleLog, IJobCancellationToken cancelToken)
{
var retryPolicy = Policy.Handle<Exception>()
.WaitAndRetry(new[]
{
TimeSpan.FromSeconds(5),
TimeSpan.FromSeconds(10),
TimeSpan.FromSeconds(15)
},
(exception, retryTime, context) =>
{
consoleLog.WriteLine($"延遲重試:{retryTime},目前時間:{DateTime.Now}");
});
retryPolicy.Execute(() => this.PollyAction(msg, consoleLog, cancelToken));
consoleLog.WriteLine($"執行完畢,目前時間:{DateTime.Now}");
}
private void PollyAction(string msg, PerformContext consoleLog, IJobCancellationToken cancelToken)
{
if (msg == "1")
{
consoleLog.WriteLine($"PollyAction 執行中,目前時間:{DateTime.Now}");
Thread.Sleep(5000);
throw new Exception("噴錯了~");
}
consoleLog.WriteLine($"執行完畢:目前時間{DateTime.Now}");
}
執行結果如下圖:
演練步驟,先傳 "1" 給 PollyRetry 方法,再傳 "2" 給 PollyRetry 方法,PollyRetry - 1 的 Queue 發生錯誤重試,不重新排隊,直到 PollyRetry - 1 重試完成才換 PollyRetry - 2 執行,截圖看不出效果,就不截了
範例專案
https://github.com/yaochangyu/sample.dotblog/tree/master/Hangfire/Lab.HangfireEnqueueRetry
延伸閱讀
[Hangfire] 如何對 Hangfire Job 撰寫測試 |
[Hangfire] Hangfire OWIN-Host 搭配 SQLite Storage |
[Hangfire] ASP.NET Core Hangfire 排程管理 - Hangfire.Dashboard.Management |
[Hangfire] 使用 Hangfire OWIN 建立非同步任務 |
若有謬誤,煩請告知,新手發帖請多包涵
Microsoft MVP Award 2010~2017 C# 第四季
Microsoft MVP Award 2018~2022 .NET