在 .NET 和 .NET Core 应用程序中执行后台处理的简单方法。无需 Windows 服务或单独的进程。由持久存储支持。开源且免费用于商业用途。
官网地址:https://www.hangfire.io/
文档地址:https://docs.hangfire.io/en/latest/
Hangfire由三个主要部分组成:客户端、存储和服务器。
Hangfire 适用于大多数 .NET 平台:.NET Framework 4.5 或更高版本、.NET Core 1.0 或更高版本,或任何与 .NET Standard 1.3 兼容的平台。
您可以将它与几乎任何应用程序框架集成,包括 ASP.NET、ASP.NET Core、控制台应用程序、Windows 服务、WCF等。
存储是 Hangfire 保存与后台作业处理相关的所有信息的地方。类型、方法名称、参数等所有细节都被序列化并放入存储中,没有数据保存在进程的内存中。存储子系统在 Hangfire 中被很好地抽象出来,可以为 RDBMS 和 NoSQL 解决方案实现。
这是开始使用框架之前所需的唯一配置。以下示例显示如何使用 SQL Server 数据库配置 Hangfire。请注意,连接字符串可能会有所不同,具体取决于您的环境。
GlobalConfiguration.Configuration
.UseSqlServerStorage(@"Server=.\SQLEXPRESS; Database=Hangfire.Sample; Integrated Security=True");
客户端可以创建任何类型的后台作业:即时任务(立即执行),延迟任务(在一段时间后执行调用),定时任务(每小时、每天执行方法等)。
Hangfire 不需要你创建特殊的类。后台作业基于常规的静态或实例方法调用。
//实例方法调用
var client = new BackgroundJobClient();
client.Enqueue(() => Console.WriteLine("Hello world!"));
//静态方法调用
BackgroundJob.Enqueue(() => Console.WriteLine("Hello world!"));
后台作业由Hangfire Server处理。它被实现为一组专用的(不是线程池的)后台线程,它们从存储中获取作业并处理它们。服务器还负责保持存储清洁并自动删除旧数据。
Hangfire 为每个存储后端使用可靠的获取算法,因此您可以在 Web 应用程序内部开始处理,而不会在应用程序重新启动、进程终止等时丢失后台作业的风险。
您只需要创建一个
BackgroundJobServer
类的实例并开始处理:
using (new BackgroundJobServer())
{
Console.WriteLine("Hangfire Server started. Press ENTER to exit...");
Console.ReadLine();
}
客户端创建任务到存储和服务器从存储拉取任务执行之间是可以分开执行的。
通过同一个存储进行交换数据,而不直接交换数据,可以实现客户端和服务器的解耦。
Hangfire 作为几个 NuGet 包分发,从主要包 Hangfire.Core 开始,它包含所有主要类和抽象。Hangfire.SqlServer 等其他包提供功能或抽象实现。要开始使用 Hangfire,请安装主软件包并选择一个可用的存储。
这里我是用MySql作为Hangfire的Storage。Hangfire 官方在免费版中只提供了 SqlServer 接入的支持,在收费版多一个 Redis。需要 MongoDB、SqlServer 、PostgreSql、SQLite 等其他 Storages 的可以自己寻找第三方的开源项目,这里有一个官方推荐的扩展清单https://www.hangfire.io/extensions.html,清单中列出了一些其他种类的 Storages。
Hangfire.Core 版本1.7.30,Net5
Hangfire.MySqlStorage mysql数据库存储
安装数据库
安装数据库可以使用docker容器化安装,简单易用,一行命令解决,docker相关知识可以参考docker详解。
docker run --name mysqlserver -v /data/mysql/conf:/etc/mysql/conf.d -v /data/mysql/logs:/logs -v /data/mysql/data:/var/lib/mysql -e MYSQL_ROOT_PASSWORD=123456 -d -i -p 3306:3306 mysql:latest --lower_case_table_names=1
参数 | 说明 |
---|---|
–name mysqlserver | 容器运行的名字 |
-v /data/mysql/conf:/etc/mysql/conf.d | 将宿主机/data/mysql/conf映射到容器/etc/mysql/conf.d |
-v /data/mysql/logs:/logs | 将宿主机/data/mysql/logs映射到容器/logs |
-v /data/mysql/data:/var/lib/mysql | 将宿主机/data/mysql/data映射到容器 /var/lib/mysql |
-e MYSQL_ROOT_PASSWORD=123456 | 数据库初始密码123456 |
-p 3306:3306 | 将宿主机3306端口映射到容器的3306端口 |
–lower_case_table_names=1 | 设置表名忽略大小写,只能首次修改,后续无法修改 |
创建数据库hangfiredb
使用
GlobalConfiguration
类执行配置。它的Configuration
属性提供了很多扩展方法,既来自 Hangfire.Core,也来自其他包。方法调用可以链式调用,因此无需一次又一次地使用类名。全局配置是为了简单起见,几乎每个 Hangfire 类都允许您为存储、过滤器等指定覆盖。在 ASP.NET Core 环境中,全局配置类隐藏在
AddHangfire
方法中。
internal class Program
{
static void Main(string[] args)
{
//配置存储
GlobalConfiguration.Configuration
.SetDataCompatibilityLevel(CompatibilityLevel.Version_170)//全局配置兼容版本,向下兼容
.UseColouredConsoleLogProvider()//输出日志
.UseSimpleAssemblyNameTypeSerializer()//使用简单程序集名称类型序列化程序
.UseRecommendedSerializerSettings()//使用推荐的序列化配置
.UseStorage(new MySqlStorage(
"server=服务器IP地址;Database=hangfiredb;user id=root;password=123456;SslMode=none",
new MySqlStorageOptions
{
TransactionIsolationLevel = IsolationLevel.ReadCommitted,
QueuePollInterval = TimeSpan.FromSeconds(15),
JobExpirationCheckInterval = TimeSpan.FromHours(1),
CountersAggregateInterval = TimeSpan.FromMinutes(5),
PrepareSchemaIfNecessary = true,
DashboardJobListLimit = 50000,
TransactionTimeout = TimeSpan.FromMinutes(1),
TablesPrefix = "Hangfire"
}));
//客户端创建任务
BackgroundJob.Enqueue(() => Console.WriteLine("Hello, world!"));
//服务器运行任务
using (var server = new BackgroundJobServer())
{
Console.ReadLine();
}
}
}
可选参数说明:
TransactionIsolationLevel
- 事务隔离级别。默认为已提交读。QueuePollInterval
- 作业队列轮询间隔。默认值为 15 秒。JobExpirationCheckInterval
- 作业过期检查间隔(管理过期记录)。默认值为 1 小时。CountersAggregateInterval
- 聚合计数器的间隔。默认为 5 分钟。PrepareSchemaIfNecessary
- 如果设置为true
,它会创建数据库表。默认为true
。DashboardJobListLimit
- 仪表板作业列表限制。默认值为 50000。TransactionTimeout
- 交易超时。默认值为 1 分钟。TablesPrefix
- 数据库中表的前缀。默认为无
即时任务作业只执行一次,几乎在创建后 立即执行。
var jobId = BackgroundJob .Enqueue(() => Console .WriteLine( "即时任务!" ));
延迟的作业也只执行一次,但不是在一定时间间隔后立即执行。
var jobId2 = BackgroundJob.Schedule(() => Console.WriteLine("延迟任务!"), TimeSpan.FromMilliseconds(10));
重复作业在指定的CRON 计划上****多次触发。
RecurringJob.AddOrUpdate("myrecurringjob", () => Console.WriteLine("重复任务!"), Cron.Minutely);
当其父作业完成时继续执行。
BackgroundJob.ContinueJobWith(jobId2,() => Console.WriteLine("jobId2执行完了再继续执行!"));
批处理是一组以原子方式创建并被视为单个实体的后台作业。
var batchId = BatchJob.StartNew(x =>
{
x.Enqueue(() => Console.WriteLine("Job 1"));
x.Enqueue(() => Console.WriteLine("Job 2"));
});
当父批处理中的所有后台作业****完成时, 将触发批处理继续。
BatchJob.ContinueBatchWith(batchId, x =>
{
x.Enqueue(() => Console.WriteLine("Last Job"));
});
业务逻辑复杂的时候可以封装到方法中
//封装的方法
public class SyncUserDataSchedule
{
public void SyncUserData()
{
Console.WriteLine($"同步用户数据--{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")}");
}
}
RecurringJob.AddOrUpdate(c => c.SyncUserData(), Cron.Minutely);
仪表盘
权限控制
http请求任务
请求参数
自定义任务
corn配置
Microsoft.AspNetCore.App
Hangfire.Core
Hangfire.MySqlStorage --mysql数据库存储
Hangfire.AspNetCore --AspNetCore支持
Hangfire.Dashboard.BasicAuthorization --可视化+权限控制
Hangfire.HttpJob --httpJob
同上快速开始,创建hangfiredb
添加连接字符串和打印日志等级
Allow User Variables=true;这个参数重要,否则页面无法正常显示。
{
"ConnectionStrings": {
"HangfireConnection":"server=服务器IP地址;Database=hangfiredb;userid=root;password=123456;SslMode=none;Allow User Variables=true;"
},
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information",
"Hangfire": "Information"
}
},
"AllowedHosts": "*"
}
账号验证也可以配置到数据库和配置文件等
public class Startup
{
public Startup(IConfiguration configuration)
{
Config = configuration;
}
public IConfiguration Config { get; }
// This method gets called by the runtime. Use this method to add services to the container.
// For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940
public void ConfigureServices(IServiceCollection services)
{
// Add Hangfire services.
services.AddHangfire(configuration => configuration
.SetDataCompatibilityLevel(CompatibilityLevel.Version_170)
.UseSimpleAssemblyNameTypeSerializer()
.UseRecommendedSerializerSettings()
.UseStorage(new MySqlStorage(
Config["ConnectionStrings:HangfireConnection"],
new MySqlStorageOptions
{
TransactionIsolationLevel = IsolationLevel.ReadCommitted,
QueuePollInterval = TimeSpan.FromSeconds(15),
JobExpirationCheckInterval = TimeSpan.FromHours(1),
CountersAggregateInterval = TimeSpan.FromMinutes(5),
PrepareSchemaIfNecessary = true,
DashboardJobListLimit = 50000,
TransactionTimeout = TimeSpan.FromMinutes(1),
TablesPrefix = "Hangfire"
})).UseHangfireHttpJob());
}
// 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();
}
app.UseHangfireServer();
app.UseHangfireDashboard("/hangfire", new DashboardOptions
{
Authorization = new[] { new BasicAuthAuthorizationFilter(new BasicAuthAuthorizationFilterOptions
{
RequireSsl = false,
SslRedirect = false,
LoginCaseSensitive = true,
Users = new []
{
new BasicAuthAuthorizationUser
{
Login = "admin",
PasswordClear = "test"
}
}
})}
});
app.UseRouting();
app.UseEndpoints(endpoints =>
{
endpoints.MapGet("/", async context =>
{
await context.Response.WriteAsync("Hello World!");
});
});
}
}
访问页面http://localhost:8848/hangfire,首次需要验证账号
登陆之后可以看到整个仪表盘和配置界面
Job管理:仪表盘主页
作业:作业的一些执行情况
重试:一些失败重试的作业信息
周期性作业:可以配置一些周期性作业
服务器:展示当前运行任务的服务器信息
其他部分同Asp.NetCore5,Program如下
public class Program
{
public static void Main(string[] args)
{
var builder = WebApplication.CreateBuilder(args);
// Add Hangfire services.
builder.Services.AddHangfire(configuration => configuration
.SetDataCompatibilityLevel(CompatibilityLevel.Version_170)
.UseSimpleAssemblyNameTypeSerializer()
.UseRecommendedSerializerSettings()
.UseStorage(new MySqlStorage(
builder.Configuration["ConnectionStrings:HangfireConnection"],
new MySqlStorageOptions
{
TransactionIsolationLevel = IsolationLevel.ReadCommitted,
QueuePollInterval = TimeSpan.FromSeconds(15),
JobExpirationCheckInterval = TimeSpan.FromHours(1),
CountersAggregateInterval = TimeSpan.FromMinutes(5),
PrepareSchemaIfNecessary = true,
DashboardJobListLimit = 50000,
TransactionTimeout = TimeSpan.FromMinutes(1),
TablesPrefix = "Hangfire"
})).UseHangfireHttpJob());
var app = builder.Build();
app.UseHangfireServer();
app.UseHangfireDashboard("/hangfire", new DashboardOptions
{
Authorization = new[] { new BasicAuthAuthorizationFilter(new BasicAuthAuthorizationFilterOptions
{
RequireSsl = false,
SslRedirect = false,
LoginCaseSensitive = true,
Users = new []
{
new BasicAuthAuthorizationUser
{
Login = "admin",
PasswordClear = "test"
}
}
})}
});
app.MapGet("/", () => "Hello World!");
app.Run();
}
}
定时任务只配置请求业务api,将业务封装到api对外暴露api,实现业务和定时任务的解耦。