使用Hangfire處理ASP.NET MVC/Web API長時間與排程工作
網頁通常是做為一個即時資訊交換工具,也就是一個請求-回應模式,不過在現實世界(或討論區)中經常會聽到希望可以進行長時間作業與排程作業的需求,網頁長時間作業通常是使用非同步技術來處理,但排程作業呢?這在單純的網頁作業流程中比較難處理,通常會勸說交給後端SQL Server排程處理或使用Windows工作排程器或寫成Windows Service來解決。
廢話那麼多,反正就是傳統網頁要處理長時間工作與排程工作都要額外花費不少心思,最近剛好有這方面的需求,試玩國外火紅Hangfire,不試還好,試了…
Hangfire - 射後不理、延遲、定時
從Hangfire官網可以看到最明顯的三個功能:Fire-and-forget tasks、Delayed tasks、Recurring tasks。
- Fire-and-forget tasks又稱射後不理任務。
- Delayed tasks又稱延遲任務。
- Recurring tasks定時任務。
就是說,Hangfire是一套背景工作處理組件(Fire-and-forget),但是又更強大,還能延遲執行(Delayed)與定時排程執行(Recurring)。
Hangfire不論免費版或付費版(Pricing)都能商業使用。
因為ASP.NET MVC 5與ASP.NET Web API 2的OWIN設置一致,程式則完全一樣,以下選擇使用ASP.NET Web API來展示。如果你是使用ASP.NET MVC 5專案,其實步驟應該都是一樣的。
Web API + Hangfire
第一步:建立ASP.NET Web API專案
這裡選擇用Empty + Web API。
第二步:NuGet安裝Hangfire
正常是要先安裝Hangfire.Core再安裝任務儲存區套件,任務儲存區是指Hangfire必須將你的任務存放至一個暫時區域,然後依照你指定的任務不同,再一一取出執行。目前支援RDBMS與NoSQL,例如:Microsoft SQL Server、redis、PostgreSQL、mongoDB、composite等。
不過做Lab就讓我偷個懶,讓我們安裝Hangfire.MemoryStorage套件,它會連Hangfire.Core一併安裝。
PM> Install-Package Hangfire.MemoryStorage
注意,MemoryStorage並不合適用於Production環境。
第三步:安裝Owin環境
Hangfire設計成使用Owin來設置與啟用,因為第一步是Empty範本,所以讓我們追加安裝Owin環境:
PM> Install-Package Microsoft.Owin.Host.SystemWeb
第四步:Hangfire Owin組態
在專案加入Owin Startup class(注意,檔案名稱請改為Startup.cs而不是預設的Startup1.cs):
using Hangfire; using Hangfire.MemoryStorage; using Microsoft.Owin; using Owin; using System; using System.Threading.Tasks; [assembly: OwinStartup(typeof(LabHangfireWebApi.Startup))] namespace LabHangfireWebApi { public class Startup { public void Configuration(IAppBuilder app) { // 指定Hangfire使用記憶體儲存任務 GlobalConfiguration.Configuration.UseMemoryStorage(); // 啟用HanfireServer app.UseHangfireServer(); // 啟用Hangfire的Dashboard app.UseHangfireDashboard(); } } }
到這個步驟就完成Hangfire的配置。
Web API+Hangfire火力展示
Hangfire Dashboard
在Owin設置有看到一個app.UseHangfireDashboard();
,它會啟用一個http://server/hangfire的Dashboard,讓我們很方便的查看目前Jobs、Retries、Recurring Jobs、Servers等狀態。
Hangfire射後不理實作
public IHttpActionResult Post(string content) { BackgroundJob.Enqueue(() => Send(content)); return Ok(); } public static void Send(string message) { Debug.WriteLine($"由Hangfire發送的訊息:{message}, 時間:{DateTime.Now}"); }
這裡假設Send()方法是一個需要長時間處理的工作,透過BackgroundJob.Enqueue()
把此任務送到背景去處理。你能發現,此方法會立即回應你Http 200狀態碼,並不會造成鎖定。
筆者用Fiddler同時間打超過1000筆請求(任務)進去,Hangfire都能秒殺且輕鬆完成。(註,這是在本機打,所以特別快:D)
Hangfire延遲實作
public IHttpActionResult Post() { Debug.WriteLine($"API現在時間:{DateTime.Now}"); BackgroundJob.Schedule( () => Debug.WriteLine($"由HangFire排程發送,時間:{DateTime.Now}"), TimeSpan.FromSeconds(3)); return Ok(); }
BackgroundJob.Schedule()
就是希望目前的任務延後執行,不過注意,以範例來說,我會得到兩個訊息,兩個訊息前後差3秒,但輸出的訊息時間會是一樣。也就是說,DateTime.Now
在送入背景等待時就已經執行完畢,而不是3秒後才被執行。
Hangfire定時實作
public IHttpActionResult Post() { RecurringJob.AddOrUpdate( () => Debug.WriteLine($"API現在時間:{DateTime.Now}") , Cron.Minutely); return Ok(); }
RecurringJob.AddOrUpdate()
是重頭戲,也就是我們以前我們在網頁上難以處理的定時、排程的處理,Cron
關鍵字如果你有玩過Linux的人應該都很熟悉,可以設置:分、小時、日、週、月、年。如果要更進一步的設置詳細時間,可以透過方法的多載來設定,例如:Daily(int hour, int minute);
可設定每天的幾點幾分來定時觸發。如果你習慣Cron表示式(CRON expressions),例如:"0 12 * */2"
,Hangfire也支援。
請問, 文中提到 "注意,MemoryStorage並不合適用於Production環境。" 是為什麼? 是擔心吃太多記憶體影響正式環境嗎? 還是什麼其他原因? 謝謝
回覆刪除作者已經移除這則留言。
刪除簡單呀,你在本機重新啟動你的專案,所有 Job 不是都不見了嗎,同理,這在"Production"還得了。
刪除那正式環境的任務要儲存在哪裡?
刪除儲存在資料庫
刪除