.net core下定時任務的實現
在上一篇[.net core下驗證碼及二維碼登錄的實現]主要介紹了驗證碼及二維碼的實現,本篇主要介紹下在 .net core下如何實現定時任務
Hangfire介紹
Hangfire作為一款高人氣且容易上手的分散式後台執行服務,支持多種資料庫。在 .net core的環境中,由Core自帶的DI管理著生命周期,免去了在NF4.X環境中配置always running的麻煩,真正做到開箱即用。
官方文檔點這裡
相較於http://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對於小項目來說用起來還是比較方便的,但對於精度要求和性能要求比較高的項目來說,還需要考量下。畢竟沒有壓測過,不知道性能怎麼樣。
推薦閱讀:
※[譯] 將一個舊的大型項目遷移到 Python 3
※木犀互聯網技術周刊(第十一期)
※去哪兒 Api 自動化測試實踐
※伺服器編程心得(四)—— 如何將socket設置為非阻塞模式