HangFire快速入门

Hangfire作为一款高人气且容易上手的分布式后台执行服务,支持多种数据库。在.net core的环境中,由Core自带的DI管理着生命周期,免去了在NF4.X环境中配置always running的麻烦,真正做到开箱即用。

安装

Hangfie官方支持是MsSql和redis,除此之外,可供选择的还有PostgreSqlMongo
在应用入口项目需要引用Hangfire.AspNetCore和特定持久库,比如使用了MsSql数据库的Hangfire.SqlServer
而在其他项目(比如bll层的项目),只需引用基础的Hangfire.Core就可以了。

Startup 配置

注入Hnagfire服务

1
services.AddHangfire(x => x.UseSqlServerStorage("<connection string>"));

可选配置

启动Hangfire服务和对应的web面板如下:

1
2
app.UseHangfireServer();//启动Hangfire服务
app.UseHangfireDashboard();//启动hangfire仪表盘

细心的观众可能会发现,这两个方法都有可选参数,可以提供更多的配置。

配置任务属性

1
2
3
4
5
6
7
var jobOptions = new BackgroundJobServerOptions
{
Queues = new[] { "test","default" },//队列名称,只能为小写
WorkerCount = Environment.ProcessorCount * 5, //并发任务数
ServerName="hangfire1",//服务器名称
};
app.UseHangfireServer(jobOptions);

Queues 要处理的队列列表

对于有多个服务器同时连接到数据库,Hangfire会认为他们是分布式中的一份子。现实中不同服务器往往存在着差异,这个时候就需要合理配置服务器(应用)的处理队列,举两个例子:

1.对于服务器性能差异的处理,有100个A任务和50个B任务需要处理,假设A服务器的性能是B服务器的两倍,如果不配置队列,那么会平分任务给两个服务器。如果我们只让B服务器处理B任务,而A服务器同时处理两种任务,这样B就能减少一些压力。

2.对于服务器能力差异的处理,假设A服务器能处理A和B两种任务,B服务器只能处理B任务(没有处理A任务的方法或对象),如果不配置队列,默认会让B也执行A任务,从而产生错误。反面一想,如果A服务器和B服务器都有共同的接口,B服务器不实现接口的方法,发起一个专属于A服务器队列的任务,而A服务器通过注入实现接口的方法,可以达到传递任务的效果。

WorkerCount 并发任务数,超出并发数将等待之前的任务完成

默认的并发任务数是线程(cpu)的5倍,如果IO密集型任务多而CPU密集型的任务少,可以考虑调高并发任务数。

以上是我用到的,当然还有其他配置参数等着你去开发。

配置访问权限

在实际生产中,我们可能不希望任何人都可以访问面板,或暂停执行某些任务,这时就需要重写面板的权限了。默认情况下,只有本地访问权限才能使用Hangfire仪表板。所以需要重写控制面板,以便远程访问。

1
2
3
4
5
6
7
8
9
10
11
12
13
var options = new DashboardOptions
{
Authorization = new[] { new HangfireAuthorizationFilter() }
};
app.UseHangfireDashboard("/hangfire", options);
public class HangfireAuthorizationFilter : IDashboardAuthorizationFilter
{
//这里需要配置权限规则
public bool Authorize(DashboardContext context)
{
    return true;
}
}

使用(官方文档)

任务类型:

Fire-and-forget 直接将任务加入到待执行任务队列
Delayed 在当前时间后的某个时间将任务加入到待执行任务队列
Recurring 周期性任务,每一个周期就将任务加入到待执行任务队列
Continuations 顾名思义,继续执行任务

简单入门

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
using (var connection = JobStorage.Current.GetConnection())
{
var storageConnection = connection as JobStorageConnection;
if (storageConnection != null)
{
//立即启动:Fire-And-forget(发布/订阅)
var jobId = BackgroundJob.Enqueue(()=>Console.WriteLine("Fire-and-forget!"));
//延迟:如果想要延迟某些任务的执行,可以是用以下任务
//在给定延迟时间后,任务会被排入队列,并且和发布/订阅任务一样执行。
var jobId =BackgroundJob.Schedule(() => Console.WriteLine("Delayed"), TimeSpan.FromDays(1));
//循环:按照周期性(小时,天等)来调用方法
//请使用RecurringJob类。在复杂的场景,您可以使用CRON表达式指定计划时间来处理任务。
var jobId =RecurringJob.AddOrUpdate(() => Console.WriteLine("Daily Job"), Cron.Daily);
//连续:连续性允许您通过将多个后台任务链接在一起来定义复杂的工作流。
var id = BackgroundJob.Enqueue(() => Console.WriteLine("Hello, "));
BackgroundJob.ContinueWith(id, () => Console.WriteLine("world!"));
}
}

当然,不仅仅只有静态方法可以执行,Hangfire的任务也是支持.net core的依赖注入的,会构造一个对象并执行对应的方法。

1
2
3
4
5
//立即执行
jobId = BackgroundJob.Enqueue<SomeClass>(a => a.SomeMethod(someParams))
//延迟任务
jobId = BackgroundJob.Schedule<SomeClass>(a => a.RunTask(someParams), RunDateTime);
//以此类推...

进阶功能

任务的新增删除:

1
2
3
4
5
//操作定时任务
RecurringJob.RemoveIfExists(JobId);
RecurringJob.AddOrUpdate(JobId, () => Method(param), Cron.Daily(hour, min));
//删除延迟任务
BackgroundJob.Delete(JobId);//删除原有任务

使用日志过滤器(点我查看)

Hangfire支持自定义过滤器,可以对任务在创建时、执行中、执行后等等状态执行特定特定的操作。

1
2
3
4
5
6
//特定方法过滤器
[LogEverything]
public static void Send() {
}
//全局过滤器
GlobalJobFilters.Filters.Add(new LogEverythingAttribute());

参考文章:[https://www.cnblogs.com/chenug/p/6655636.html]