1、HangFire簡介
HangFire是一個免費簡單實用的分布式后台定時調度服務,在現在.net開發中,人氣算是很高的。
HangFire提供了內置集成化的控制台,可以直觀明了的查看作業調度情況,並且Hangfire不需要依賴於單獨的應用程序執行,支持持久性存儲,默認使用sqlserver。
2、HangFire安裝
一般的,我們都是使用nuget安裝HangFire
Install-Package Hangfire
個人不建議這么安裝,最好是根據自己的需求來,因為Hangfire默認使用SqlServer作為存儲數據庫,如果我們使用mysql,就沒有必要了,而且最新的Hangfire會依賴於Hangfire.AspNetCore,方便在.net core項目中使用,本文以mysql作為數據庫來介紹,如果我們用的是.net core項目,安裝
Install-Package Hangfire.AspNetCore Install-Package Hangfire.MySql.Core
如果是控制台程序,安裝
Install-Package Hangfire.MySql.Core
3、HangFire例子
using Hangfire; using Hangfire.MySql.Core; using System; using System.Threading; namespace HangFire { class Program { static void Main(string[] args) { //創建表 var storage = new MySqlStorage("Server=192.168.220.129;port=3306;Database=hangfire;User ID=root;Password=123456;charset=utf8;Allow User Variables=true"); //使用mysql GlobalConfiguration.Configuration.UseStorage(storage); //添加循環執行的定時任務 RecurringJob.AddOrUpdate(() => Console.WriteLine("Hangfire AddOrUpdate任務"), "1/5 * * * * *", TimeZoneInfo.Local, "queue1"); //后台調度任務 BackgroundJobServer jobServer = new BackgroundJobServer(new BackgroundJobServerOptions() { Queues = new string[] { "queue1", "queue2" }, ServerName = "Test", WorkerCount = 1 }, storage); //等待退出 //jobServer.WaitForShutdownAsync(CancellationToken.None); Console.ReadLine(); } } }
4、HangFire調度用法
BackgroundJobServer用於啟動調度,如上面的例子,
BackgroundJobServer jobServer = new BackgroundJobServer(new BackgroundJobServerOptions() { Queues = new string[] { "queue1", "queue2" }, ServerName = "Test", WorkerCount = 1 }, storage);
需要注意的是Queues參數,它是BackgroundJobServer會調度的隊列,默認隊列名是default,上面的例子中沒有將default添加到隊列中去,因此所有使用default默認隊列的任務將不會被調度執行,所以建議將default也加進入,即Queues = new string[] { "default", "queue1", "queue2" }。
重點說的是BackgroundJob和RecurringJob兩個類,這兩個類都是進行調度任務的操作
BackgroundJob
BackgroundJob主要用於單次任務的調度
//添加任務到隊列,會返回任務Id BackgroundJob.Enqueue(() => Console.WriteLine("Hangfire Enqueue任務")); //支持異步 BackgroundJob.Enqueue(() => MyTask.LogAsync()); //計划執行,即在指定的時間執行 BackgroundJob.Schedule(() => Console.WriteLine("Hangfire Schedule任務"), TimeSpan.FromSeconds(10));//延遲10秒后執行 //支持異步 BackgroundJob.Schedule(() => MyTask.LogAsync(), TimeSpan.FromSeconds(10));//延遲10秒后執行
其中MyTask是一個類
public class MyTask { public static async Task LogAsync() { await Task.Run(() => Console.WriteLine("MyTask")); } }
Enqueue用於將任務立即添加到執行隊列等待執行,Schedule用於延遲添加,即延遲指定時間后將任務添加到執行度列。要注意是,這里是添加到默認隊列,也就是default隊列,如果在啟動server時,未添加default隊列,那么將不會被調度執行。而且添加的任務只會被執行一次,另外,這里只是將任務加入到默認隊列中,執行還需要排隊。Enqueue還有一個返回值,即jobId,它表示添加任務得到的任務Id,我們可以通過這個jobId對隊列進行移除,重新進入隊列等等操作。
//添加任務到隊列,返回任務Id var jobId = BackgroundJob.Enqueue(() => Console.WriteLine("Hangfire Enqueue任務")); //指定任務成功執行完再執行,同樣返回新增的任務Id var otherJobId = BackgroundJob.ContinueJobWith(jobId, () => Console.WriteLine("Hangfire ContinueJobWith任務")); //移除 BackgroundJob.Delete(otherJobId); //重新進入隊列 BackgroundJob.Requeue(otherJobId);
RecurringJob
RecurringJob主要用於定時循環的任務調度
//添加循環執行的定時任務 RecurringJob.AddOrUpdate(() => Console.WriteLine("Hangfire AddOrUpdate任務"), "*/5 * * * * *", TimeZoneInfo.Local, "queue1"); //添加循環執行的定時任務,同時執行recurringJobId RecurringJob.AddOrUpdate("myRecurringJobId", () => Console.WriteLine("Hangfire AddOrUpdate任務"), "*/5 * * * * *", TimeZoneInfo.Local, "queue1");
這里說下recurringJobId,其實這個就是這個定時任務的名稱,AddOrUpdate就是根據這個recurringJobId來判斷是新增還是更新的,如果不指定recurringJobId,則會自動根據Lambda表達式來生成一個recurringJobId,比如上面會用Console.WriteLine作為recurringJobId,這個recurringJobId還可以用於移除任務和立即觸發:
//移除任務 RecurringJob.RemoveIfExists("myRecurringJobId"); //立即觸發一次 RecurringJob.Trigger("myRecurringJobId");
RecurringJob采用Cron表達式來指明調度時間,這個也是Hangfire的一個優勢。
另外提一點,BackgroundJob的Enqueue方法和Schedule方法,RecurringJob的AddOrUpdate方法都有一個泛型的重載,上面的例子都是靜態方法,無需調用對象,而這個泛型其實就是任務的調用對象,如:
BackgroundJob.Enqueue<TestModel>(tm => tm.Show());
其中TestModel是
public class TestModel { public string Text { get; set; } = "Test"; public void Show() { Console.WriteLine(Text); } }
默認情況下,任務的調用實例是JobActivator對象創建的,默認創建方式就是
public virtual object ActivateJob(Type jobType) { return Activator.CreateInstance(jobType); }
因此,這個調用對象必須是有空構造函數,否則,我們需要使用繼承去重寫JobActivator的ActivateJob方法,再使用GlobalConfiguration.Configuration.UseActivator();方法將重寫的JobActivator集成進去,這種方式也可以用於IOC容器的支持。
5、.net core 使用HangFire
.net core使用hangfire很簡單,首先安裝Hangfire.AspNetCore和Hangfire.MySql.Core,然后在Startup中集成即可
using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Hangfire.MySql.Core; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.HttpsPolicy; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; namespace Hangfire.Web { public class Startup { public Startup(IConfiguration configuration) { Configuration = configuration; } public IConfiguration Configuration { get; } // This method gets called by the runtime. Use this method to add services to the container. public void ConfigureServices(IServiceCollection services) { //添加Hangfire服務 var storage = new MySqlStorage("Server=192.168.220.129;port=3306;Database=hangfire;User ID=root;Password=123456;charset=utf8;Allow User Variables=true"); services.AddHangfire(cfg => { cfg.UseStorage(storage); }); //以IHostedService的形式啟動BackgroundJobServer //如果這里添加了,那么Configure方法中就無需調用app.UseHangfireServer(); //services.AddHangfireServer(); services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2); } // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. public void Configure(IApplicationBuilder app, IHostingEnvironment env) { if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } else { // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts. app.UseHsts(); } //配置后台儀表盤 app.UseHangfireDashboard(); //開始使用Hangfire服務 //如果這里添加了中間件,那么ConfigureServices中就無需調用services.AddHangfireServer(); app.UseHangfireServer(); app.UseHttpsRedirection(); app.UseMvc(); } } }
然后啟動之后,可以在瀏覽器輸入http://localhost:5000/hangfire打開Hangfire的儀表盤