什么是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();
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



