上一篇「背景常駐執行計時工作 - 使用 BackgroundService 與 PeriodicTimer」介紹了使用 BackgroundService 與 PeriodicTimer 實作計時的工作處理。
但如果有時候一些工作處理並不是每幾秒鐘或每幾分鐘、每幾個小時這樣的週期行為時,使用 PeriodicTimer 就無法滿足這樣的需求,而在使用 Hangfire 建立 Recurring Job 時都會使用到 Cron Expression 去定義工作的執行週期,而就有看到這麼一個 NuGet 套件就有提供這樣的功能,所以就拿來試試看。
Cron
"cron" 其實是 「chronograph」 的縮寫或變體。這個詞源自於希臘語「χρόνος」(chronos),意思是時間。cron 是一個用於類 Unix 系統的時間驅動任務排程工具,能在指定的時間自動執行程式或腳本。
cron 的名字反映了它的功能:基於時間的排程與自動化。例如,使用 cron,你可以設置在每天的特定時間執行某個指令或腳本。這種時間驅動的特性使它在伺服器管理、定時任務執行等方面非常有用。
因此,雖然 "cron" 並不是一個正式的縮寫,而是來源於「chronos」,其名字的選擇恰好描述了它的作用。
在設定 Cron Expression 時,我都習慣使用 crontab guru 這個線上工具做編輯,因為可以即時地在網頁上就可以得知 expression 的週期是否符合我們的預期,使用 Hangfire 建立 Recurring Job 時就會用到 Cron Expression 定義時間週期。
Cronos
Cronos 是由 Hangfire 團隊開發的一個開源 .NET Library,專門用於處理 Cron 表達式。
以下是它的主要特性與功能介紹:
- 主要功能
處理 Cron 表達式:提供簡單且靈活的 API,可以解析和評估 Cron 表達式,用於計時執行任務。
支持時區:內建對時區的處理能力,讓 Cron 表達式能夠正確應用於不同時區。 - 應用場景
用於需要基於 Cron Expression 執行任務的 .NET 應用服務,例如週期性資料備份、定時發送郵件等。
在類似 Hangfire 的背景工作系統中,精確地安排作業執行。
在專案裡並不需要安裝使用 Hangfire 時,可以透過 Cronos 這個工具來取得週期時間,來試試看
例如,我想要列出從現在開始的一個小時內每五分鐘的時間(At every 5th minute)
或是要列出從現在開始的一個月,每 6 小時, 在 12:00 AM 和 08:59 PM 之間, 只有星期一和星期五的日期時間(At minute 0 past every 6th hour from 0 through 20 on Monday and Friday)
Cronos 是可以支援到「秒」的週期,例如想要取得從現在開始到 10 分鐘內,偶數分鐘每 30 秒的時間
以往要取得這種週期循環的日期時間會比較麻煩一些,但透過 Cronos 就可以比較方便一些。
而會先介紹 Cron Expression 以及 Cronos,則是因為 Sgbj.Cron.CronTimer 是相依 Cronos 的一個工具套件,所以就先做個介紹說明。
Sgbj.Cron.CronTimer
- https://www.nuget.org/packages/Sgbj.Cron.CronTimer
- https://github.com/sgbj/crontimer
- Cron jobs and scheduled tasks in ASP.NET Core | by Scott Batary | batary | Medium
直接看它的說明就知道這是用來做什麼事情
接著就用 BackgroundService 和 CronTimer 來做一個週期的工作處理,週期時間定義就用「從現在開始,偶數分鐘每 12 秒的時間就寫一次 Log」
using Cronos;
using Sgbj.Cron;
namespace Sample.WebApplication.BackgroundServices;
/// <summary>
/// class CronTimeerBackgroundService
/// </summary>
public class CronTimeerBackgroundService : BackgroundService
{
private readonly ILogger<CronTimeerBackgroundService> _logger;
private readonly TimeProvider _timeProvider;
/// <summary>
/// Initializes a new instance of the <see cref="CronTimeerBackgroundService"/> class
/// </summary>
/// <param name="logger">The logger</param>
/// <param name="timeProvider">The timeProvider</param>
public CronTimeerBackgroundService(ILogger<CronTimeerBackgroundService> logger, TimeProvider timeProvider)
{
this._logger = logger;
this._timeProvider = timeProvider;
}
/// <summary>
/// Executes the stopping token
/// </summary>
/// <param name="stoppingToken">The stopping token</param>
/// <exception cref="NotImplementedException"></exception>
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
this._logger.LogInformation(
"{DateTimeNow} [{MachineName}] {TypeName} is starting.",
$"{this._timeProvider.GetLocalNow().DateTime:yyyy-MM-dd HH:mm:ss}",
Environment.MachineName,
this.GetType().Name);
// cron expression 每個偶數分數裡的每 12 秒
var cronExpression = CronExpression.Parse("*/12 */2 * * * *", CronFormat.IncludeSeconds);
// 使用 CronTimer
using var cronTimer = new CronTimer(cronExpression, TimeZoneInfo.Local);
while (await cronTimer.WaitForNextTickAsync(stoppingToken) && stoppingToken.IsCancellationRequested is false)
{
this._logger.LogInformation(
"{DateTimeNow} [{MachineName}] {TypeName} Processing.",
$"{this._timeProvider.GetLocalNow().DateTime:yyyy-MM-dd HH:mm:ss}",
Environment.MachineName,
this.GetType().Name);
}
}
/// <summary>
/// Stops the cancellation token
/// </summary>
/// <param name="cancellationToken">The cancellation token</param>
public override async Task StopAsync(CancellationToken cancellationToken)
{
this._logger.LogInformation(
"{DateTimeNow} [{MachineName}] {TypeName} is stopping.",
$"{this._timeProvider.GetLocalNow().DateTime:yyyy-MM-dd HH:mm:ss}",
Environment.MachineName,
this.GetType().Name);
await base.StopAsync(cancellationToken);
}
}
觀察執行的狀況,的確是只有在偶數分鐘的每 12 秒執行寫 Log
完成!
相關內容
- Cron Expression Descriptor | Github
- https://bradymholt.github.io/cron-expression-descriptor/
- NCrontab: Crontab for .NET | Github
- Cron任务调度CronNET - HackerVirus - 博客园
- 在 .NET中使用Cronos实现计划任务功能 | 微信公众号:【架构师老卢】
- 使用原生 HostedService 來進行 Schedule Job - Yowko's Notes
以上
純粹是在寫興趣的,用寫程式、寫文章來抒解工作壓力