.net core下定時任務的實現
在上一篇[.net core下驗證碼及二維碼登入的實現]主要介紹了驗證碼及二維碼的實現,本篇主要介紹下在 .net core下如何實現定時任務
Hangfire介紹
Hangfire作為一款高人氣且容易上手的分散式後臺執行服務,支援多種資料庫。在 .net core的環境中,由Core自帶的DI管理著生命週期,免去了在NF4.X環境中配置always running的麻煩,真正做到開箱即用。
官方文件點這裡
相較於quartz.net相比,最大的優點是有個自帶的監控介面,比較方便。但有一點,Hangfire只支援分鐘級別的定時任務,如果想用秒級別的定時任務,那可能Hangfire就不滿足你的需求了。
Hangfire基礎
基於佇列的任務處理(Fire-and-forget jobs)
基於佇列的任務處理是Hangfire中最常用的,客戶端使用BackgroundJob類的靜態方法Enqueue來呼叫,傳入指定的方法(或是匿名函式),Job Queue等引數.(類似MQ)
var jobId = BackgroundJob.Enqueue(
() => Console.WriteLine("Fire-and-forget!"));
在任務被持久化到資料庫之後,Hangfire服務端立即從資料庫獲取相關任務並裝載到相應的Job Queue下,在沒有異常的情況下僅處理一次,若發生異常,提供重試機制,異常及重試資訊都會被記錄到資料庫中,通過Hangfire控制面板可以檢視到這些資訊。
延遲任務執行(Delayed jobs)
延遲(計劃)任務跟佇列任務相似,客戶端呼叫時需要指定在一定時間間隔後呼叫:
var jobId = BackgroundJob.Schedule(
() => Console.WriteLine("Delayed!"),
TimeSpan.FromDays(7));
定時任務執行(Recurring jobs)
定時(迴圈)任務代表可以重複性執行多次,支援CRON表示式:
RecurringJob.AddOrUpdate(
() => Console.WriteLine("Recurring!" ),
Cron.Daily);
延續性任務執行(Continuations)
延續性任務類似於.NET中的Task,可以在第一個任務執行完之後緊接著再次執行另外的任務:
BackgroundJob.ContinueWith(
jobId,
() => Console.WriteLine("Continuation!"));
程式碼實現
在我的專案中,實際只用到了定時任務(用於跑一些報表,郵件預警之類的需求),其他的業務場景感覺還是MQ更加適合。
下面來看看 .net core下具體的程式碼實現:
首先需要引用元件Hangfire.dll
及Hangfire.MySqlStorage.dll
,我使用的是Mysql。
在Startup.cs
中的ConfigureServices
中初始化資料庫:
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
services.AddHangfire(x => x.UseStorage(new MySqlStorage(CONNECTION_STRING)));
}
在Configure
啟動你的Hangfire服務:
var jobOptions = new BackgroundJobServerOptions
{
//Queues = new[] { "test", "default" },//佇列名稱,只能為小寫
WorkerCount = Environment.ProcessorCount * 5, //併發任務數
ServerName = "hangfire1",//伺服器名稱
};
app.UseHangfireServer(jobOptions);//啟動Hangfire服務
同時你可以在Configure
下啟動你的監控應用:
var options = new DashboardOptions
{
Authorization = new[] { new HangfireAuthorizationFilter() }
};
app.UseHangfireDashboard("/job_dashboard", options);
這樣啟動後就可以看到你的監控後臺了,輸入地址/job_dashboard
一些小改動
由於專案可能經常會重新部署,所以在專案啟動時我會預設重新啟動定時任務:
在Startup.cs
直接啟動JobService.Register()
:
public static async void Register()
{
var jobKeys =await JobMonitorServices.GetAllJobKey();
if (!jobKeys.Any())
return;
//暫時只開一個queue,後期可擴充套件
foreach(var keyModel in jobKeys)
{
var key = keyModel.Key.Split(':')[1];
RecurringJob.AddOrUpdate(key, () => JobMonitorServices.Execute(key), keyModel.Value);
}
}
至於GetAllJobKey
方法,是我直接從資料庫裡取的:
public static async Task<List<HashModel>> GetAllJobKey()
{
string sql = $@"SELECT distinct `Key`,`Field`,`Value` FROM Hash WHERE Field='Cron';";
using (var conn = DatabaseManager.GetConnection(DatabaseManager.JOB_DBName))
{
await conn.OpenAsync();
return (await conn.QueryAsync<HashModel>(sql)).ToList() ;
}
}
這樣的話我可以同時暴露出對應的新增修改job介面了,這樣方便我們直接通過服務去新增job,或者修改job的觸發時間:
/// <summary>
/// 新增or更新Job
/// </summary>
/// <param name="entity"></param>
/// <returns></returns>
[HttpPost]
public IActionResult Post([FromBody]JobKeyRequestModel entity)
{
var result = JobMonitorServices.CheckEnableJobKey(entity);
if (result.Result)
RecurringJob.AddOrUpdate(entity.Key, () => JobMonitorServices.Execute(entity.Key), entity.Cron,TimeZoneInfo.Local);
return AssertNotFound(result);
}
總結
Hangfire對於小專案來說用起來還是比較方便的,但對於精度要求和效能要求比較高的專案來說,還需要考量下。畢竟沒有壓測過,不知道效能怎麼樣。