什么是Hangfire
Hangfire是一个开源且商业免费使用的工具函数库。可以让你非常容易地在ASP.NET应用(也可以不在ASP.NET应用)中执行多种类型的后台任务,而无需自行定制开发和管理基于Windows Service后台任务执行器。且任务信息可以被持久保存。内置提供集成化的控制台。
很大的原因在于项目需要一个后台可监控的应用,不用每次都要从服务器拉取日志查看,在没有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(); 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_logger; public Itest _test { get; set; } public RecurringJobsService( [NotNull] IBackgroundJobClient backgroundJobs, [NotNull] IRecurringJobManager recurringJobs, [NotNull] ILogger 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 ("seconds", i => _test.demo(), "*/1 * * * * *", queue: "critical"); //_backgroundJobs.Enqueue (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 { ////// 已完成的job设置过期,防止数据无限增长 /// 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