之前使用Quartz.Net,后來發現hangfire對Core的繼承更加的好,而且自帶管理后台,這就比前者好用太多了。
安裝注冊
安裝
PM> Install-Package Hangfire
Startup.cs,在ConfigureServices
方法中添加注冊:
services.AddHangfire(x => x.UseSqlServerStorage("connection string"));
SqlServer是使用這種方式,其他方式見官方的文檔及相應插件。
注冊完成后,還需要在Configure方法中,添加如下高亮部分的代碼:
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory) { if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } else { app.UseExceptionHandler("/Home/Error"); } //添加Hangfire app.UseHangfireServer(); app.UseHangfireDashboard();
配置完畢后運行我們的項目,這時Hangfire會自動在數據庫中創建結構,數據庫中會新建如下表:

現在在網站根目錄+/Hangfire即可查看管理后台界面如下:
基本使用
Hangfire的使用非常簡單,基本上使用以下幾個靜態方法:
//執行后台腳本,僅執行一次 BackgroundJob.Enqueue(() => Console.WriteLine("Fire-and-forget!")); //延遲執行后台腳本呢,僅執行一次 BackgroundJob.Schedule( () => Console.WriteLine("Delayed!"), TimeSpan.FromDays(7)); //周期性任務 RecurringJob.AddOrUpdate( () => Console.WriteLine("Recurring!"), Cron.Daily); //等上一任務完成后執行 BackgroundJob.ContinueWith( jobId, //上一個任務的jobid () => Console.WriteLine("Continuation!"));
注意,周期性使用可以使用Cron表達式。
# ┌───────────── minute (0 - 59) # │ ┌───────────── hour (0 - 23) # │ │ ┌───────────── day of the month (1 - 31) # │ │ │ ┌───────────── month (1 - 12) # │ │ │ │ ┌───────────── day of the week (0 - 6) (Sunday to Saturday; # │ │ │ │ │ 7 is also Sunday on some systems) # │ │ │ │ │ # │ │ │ │ │ # * * * * * command to execute
Entry | Description | Equivalent to |
---|---|---|
@yearly (or @annually) |
每年1月3號2:01分運行 | 1 2 3 1 * |
@monthly |
每月3號2:01分運行 | 1 2 3 * * |
@weekly |
每周日的2:01分運行 | 1 2 * * 0 |
@daily |
每天的2:01分運行 | 1 2 * * * |
@hourly |
每小時的1分運行 | 1 * * * * |
@reboot |
Run at startup | N/A |
依賴注入
在.Net Core中處處是DI,一不小心,你會發現你在使用Hangfire的時候會遇到各種問題,比如下列代碼:
public class HomeController : Controller { private ILogger<HomeController> _logger; public HomeController(ILoggerFactory loggerFactory) { _logger = loggerFactory.CreateLogger<HomeController>(); } public IActionResult Index() { _logger.LogInformation("start index"); BackgroundJob.Enqueue(() => _logger.LogInformation("this a job!")); return View(); } }
項目啟動后,你能正常訪問,但在Hangfire后台你會看到如下錯誤:
錯誤信息呢大概意思是不能使用接口或者抽象方法類,其實就是因為Hangfire沒有找到實例,那如何讓Hangfire支持DI呢?
我們先創建一個MyActivator
類,使其繼承Hangfire.JobActivator
類,代碼如下:
public class MyActivator : Hangfire.JobActivator { private readonly IServiceProvider _serviceProvider; public MyActivator(IServiceProvider serviceProvider) => _serviceProvider = serviceProvider; public override object ActivateJob(Type jobType) { return _serviceProvider.GetService(jobType); } }
重寫了ActivateJob方法,使其返回的類型從我們的IServiceProvider中獲取。
我們試着寫兩個后台腳本,CheckService和TimerService,CheckService的Check方法在執行計划時,會再次調用Hangfire來定時啟動TimerService:
CheckService:
public interface ICheckService { void Check(); } public class CheckService : ICheckService { private readonly ILogger<CheckService> _logger; private ITimerService _timeservice; public CheckService(ILoggerFactory loggerFactory, ITimerService timerService) { _logger = loggerFactory.CreateLogger<CheckService>(); _timeservice = timerService; } public void Check() { _logger.LogInformation($"check service start checking, now is {DateTime.Now}"); BackgroundJob.Schedule(() => _timeservice.Timer(), TimeSpan.FromMilliseconds(30)); _logger.LogInformation($"check is end, now is {DateTime.Now}"); } }
TimerService:
public interface ITimerService { void Timer(); } public class TimerService : ITimerService { private readonly ILogger<TimerService> _logger; public TimerService(ILoggerFactory loggerFactory) { _logger = loggerFactory.CreateLogger<TimerService>(); } public void Timer() { _logger.LogInformation($"timer service is starting, now is {DateTime.Now}"); _logger.LogWarning("timering"); _logger.LogInformation($"timer is end, now is {DateTime.Now}"); } }
目前還無法使用,我們必須在Startup
中注冊這2個service:
services.AddScoped<ITimerService, TimerService>();
services.AddScoped<ICheckService, CheckService>();
我們在HomeController
修改以下:
public IActionResult Index() { _logger.LogInformation("start index"); BackgroundJob.Enqueue<ICheckService>(c => c.Check()); return View(); }
好,一切就緒,只差覆蓋原始的Activator了,我們可以在Startup.cs
中的Configure
方法中使用如下代碼:
public void Configure(IApplicationBuilder app, IHostingEnvironment env, IServiceProvider serviceProvider) { GlobalConfiguration.Configuration.UseActivator<MyActivator>(new MyActivator(serviceProvider)); …… …… }
默認情況下Configure方法時沒有IServiceProvider參數的,請手動添加
再次啟動,我們的Job就會成功執行,截圖如下:
補充:以上在開發環境可以正常使用,一旦發布到正式環境會報401 Unauthorized
未授權錯誤,原因是 Hangfire 默認增加了授權配置。
解決方式:
增加CustomAuthorizeFilter
:
public class CustomAuthorizeFilter : IDashboardAuthorizationFilter { public bool Authorize([NotNull] DashboardContext context) { //var httpcontext = context.GetHttpContext(); //return httpcontext.User.Identity.IsAuthenticated; return true; } }
Configure
增加配置:
app.UseHangfireDashboard("/hangfire", new DashboardOptions() { Authorization = new[] { new CustomAuthorizeFilter() } });
參考資料
- Hangfire 官網:https://www.hangfire.io/
- Hangfire DI in .net core : https://stackoverflow.com/questions/41829993/hangfire-dependency-injection-with-net-core
- Demo 地址:https://github.com/JamesYing/BlogsRelatedCodes/tree/master/hangfireDemo