什么是Hangfire
Hangfire是一個開源且商業免費使用的工具函數庫。可以讓你非常容易地在ASP.NET應用(也可以不在ASP.NET應用)中執行多種類型的后台任務,而無需自行定制開發和管理基於Windows Service后台任務執行器。且任務信息可以被持久保存。內置提供集成化的控制台。
Hangfire的基本特征與優點
與quartz.net對比
很大的原因在於項目需要一個后台可監控的應用,不用每次都要從服務器拉取日志查看,在沒有ELK的時候相當不方便。Hangfire控制面板不僅提供監控,也可以手動的觸發執行定時任務
HangFire例子
1、新建項目
2、引用安裝
這里我們選擇redis 作為持久化方式,所以引用
Hangfire.Redis.StackExchange.StrongName
配置面板、redis鏈接
Startup.cs
public void ConfigureServices(IServiceCollection services) { services.AddControllersWithViews().AddControllersAsServices(); GlobalStateHandlers.Handlers.Add(new SucceededStateExpireHandler(int.Parse(Configuration["Hangfire:JobExpirationTimeout"]))); services.AddHostedService<RecurringJobsService>(); services.AddHangfire(x => { var connectionString = Configuration["Hangfire:Redis:ConnectionString"]; x.UseRedisStorage(connectionString, new RedisStorageOptions() { //活動服務器超時時間 InvisibilityTimeout = TimeSpan.FromMinutes(60), Db = int.Parse(Configuration["Hangfire:Redis:Db"]) }); x.UseDashboardMetric(DashboardMetrics.ServerCount) .UseDashboardMetric(DashboardMetrics.RecurringJobCount) .UseDashboardMetric(DashboardMetrics.RetriesCount) .UseDashboardMetric(DashboardMetrics.AwaitingCount) .UseDashboardMetric(DashboardMetrics.EnqueuedAndQueueCount) .UseDashboardMetric(DashboardMetrics.ScheduledCount) .UseDashboardMetric(DashboardMetrics.ProcessingCount) .UseDashboardMetric(DashboardMetrics.SucceededCount) .UseDashboardMetric(DashboardMetrics.FailedCount) .UseDashboardMetric(DashboardMetrics.EnqueuedCountOrNull) .UseDashboardMetric(DashboardMetrics.FailedCountOrNull) .UseDashboardMetric(DashboardMetrics.DeletedCount); }); }
配置面板登錄權限、作業通道
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline. public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } else { app.UseExceptionHandler("/Home/Error"); } app.UseStaticFiles(); app.UseRouting(); app.UseAuthorization(); var filter = new BasicAuthAuthorizationFilter( new BasicAuthAuthorizationFilterOptions { SslRedirect = false, RequireSsl = false, LoginCaseSensitive = false, Users = new[] { new BasicAuthAuthorizationUser { Login = Configuration["Hangfire:Login"] , PasswordClear= Configuration["Hangfire:PasswordClear"] } } }); app.UseHangfireDashboard("", new DashboardOptions { Authorization = new[] { filter }, }); var jobOptions = new BackgroundJobServerOptions { Queues = new[] { "critical", "test", "default" }, WorkerCount = Environment.ProcessorCount * int.Parse(Configuration["Hangfire:ProcessorCount"]), ServerName = Configuration["Hangfire:ServerName"], SchedulePollingInterval = TimeSpan.FromSeconds(1), //計划輪詢間隔 支持任務到秒 }; app.UseHangfireServer(jobOptions); }
{ "Logging": { "LogLevel": { "Default": "Information", "Microsoft": "Warning", "Microsoft.Hosting.Lifetime": "Information" } }, "Hangfire": { "Redis": { "ConnectionString": "localhost:6379,password=123456,abortConnect=false", "Db": 10 }, "Login": "admin", //賬號 "PasswordClear": "123456", //密碼 "ServerName": "hangfire001", //站點服務名稱 "JobExpirationTimeout": 1, //成功job過期時間 分鍾 "ProcessorCount": 5 //線程數 }, "AllowedHosts": "*" }
使用作業
using Hangfire; using Hangfire.Server; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; using System; using System.Diagnostics.CodeAnalysis; using System.Threading; using System.Threading.Tasks; using TestService; namespace hangfiretest.RecurringJobs { internal class RecurringJobsService : BackgroundService { private readonly IBackgroundJobClient _backgroundJobs; private readonly IRecurringJobManager _recurringJobs; private readonly ILogger<RecurringJobScheduler> _logger; public Itest _test { get; set; } public RecurringJobsService( [NotNull] IBackgroundJobClient backgroundJobs, [NotNull] IRecurringJobManager recurringJobs, [NotNull] ILogger<RecurringJobScheduler> logger) { _backgroundJobs = backgroundJobs ?? throw new ArgumentNullException(nameof(backgroundJobs)); _recurringJobs = recurringJobs ?? throw new ArgumentNullException(nameof(recurringJobs)); _logger = logger ?? throw new ArgumentNullException(nameof(logger)); } protected override Task ExecuteAsync(CancellationToken stoppingToken) { try { _recurringJobs.AddOrUpdate<Itest>("seconds", i => _test.demo(), "*/1 * * * * *", queue: "critical"); //_backgroundJobs.Enqueue<Services>(x => x.LongRunning(JobCancellationToken.Null)); //_recurringJobs.AddOrUpdate("seconds", () => Console.WriteLine("Hello, seconds!"), "*/15 * * * * *"); //_recurringJobs.AddOrUpdate("minutely", () => Console.WriteLine("Hello, world!"), Cron.Minutely); //_recurringJobs.AddOrUpdate("hourly", () => Console.WriteLine("Hello"), "25 15 * * *"); //_recurringJobs.AddOrUpdate("neverfires", () => Console.WriteLine("Can only be triggered"), "0 0 31 2 *"); //_recurringJobs.AddOrUpdate("Hawaiian", () => Console.WriteLine("Hawaiian"), "15 08 * * *", TimeZoneInfo.FindSystemTimeZoneById("Hawaiian Standard Time")); //_recurringJobs.AddOrUpdate("UTC", () => Console.WriteLine("UTC"), "15 18 * * *"); //_recurringJobs.AddOrUpdate("Russian", () => Console.WriteLine("Russian"), "15 21 * * *", TimeZoneInfo.Local); } catch (Exception e) { _logger.LogError("An exception occurred while creating recurring jobs.", e); } return Task.CompletedTask; } } }
已完成的job設置過期,防止數據無限增長
using Hangfire.States; using Hangfire.Storage; using System; namespace hangfiretest { /// <summary> /// 已完成的job設置過期,防止數據無限增長 /// </summary> public class SucceededStateExpireHandler : IStateHandler { public TimeSpan JobExpirationTimeout; public SucceededStateExpireHandler(int jobExpirationTimeout) { JobExpirationTimeout = TimeSpan.FromMinutes(jobExpirationTimeout); } public string StateName => SucceededState.StateName; public void Apply(ApplyStateContext context, IWriteOnlyTransaction transaction) { context.JobExpirationTimeout = JobExpirationTimeout; } public void Unapply(ApplyStateContext context, IWriteOnlyTransaction transaction) { } } }
RUN起來
是不是很簡單啊,以前我是使用quartz,自從使用了hangfire,反正我是再也不想在使用quartz;有點類似git和svn的關系;
完整代碼:
https://github.com/conanl5566/mydemo/tree/master/hangfire/hangfire.demo