.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.dllHangfire.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對於小項目來說用起來還是比較方便的,但對於精度要求和性能要求比較高的項目來說,還需要考量下。畢竟沒有壓測過,不知道性能怎麼樣。

推薦閱讀:

[譯] 將一個舊的大型項目遷移到 Python 3
木犀互聯網技術周刊(第十一期)
去哪兒 Api 自動化測試實踐
伺服器編程心得(四)—— 如何將socket設置為非阻塞模式

TAG:NET | 後端技術 | 自學編程 |