🔍 一、含义
Hangfire 是一个 .NET 后台任务处理库,允许开发者在 ASP.NET、控制台应用或 Windows 服务中创建和管理后台作业(如定时任务、队列任务)。它通过持久化存储(如 SQL Server、Redis)保存任务状态,确保应用重启后任务不丢失,并内置 Web 仪表盘(Dashboard)实现任务可视化监控
🔍 二、核心作用
1.任务调度与执行
- 即时任务(Fire-and-forget):异步执行,如日志记录、邮件发送
BackgroundJob.Enqueue(() => Console.WriteLine("Task executed!"));
- 延迟任务(Delayed):指定未来时间执行,如订单超时取消。
BackgroundJob.Schedule(() => SendReminder(), TimeSpan.FromHours(24));
- 周期性任务(Recurring):基于 Cron 表达式定时执行,如每日数据备份。
RecurringJob.AddOrUpdate("daily-job", () => SyncData(), Cron.Daily);
- 连续性任务(Continuations):任务链式执行,如支付成功后通知发货
2.持久化与可靠性
任务信息存储于数据库(SQL Server/Redis 等),应用崩溃或重启后自动恢复。
内置重试机制:任务失败时自动重试(可配置次数)
3.分布式支持
支持多节点部署,实现高可用和负载均衡。
可通过 Redis 提升性能,适应高并发场景
4.可视化监控
内置 Dashboard 实时展示任务状态、日志和执行历史,支持手动触发或取消任务
5.扩展性与集成
支持依赖注入(如 Autofac、Ninject)。
可配置多队列优先级和并发控制
任务类型 | 适用场景 | 代码示例 |
---|---|---|
即时任务 | 异步日志、邮件通知 | BackgroundJob.Enqueue(() => Log("Info")) |
延迟任务 | 订单超时处理、定时提醒 | BackgroundJob.Schedule(() => CancelOrder(), TimeSpan.FromMinutes(30)) |
周期性任务 | 每日数据备份、报表生成 | RecurringJob.AddOrUpdate("backup", () => BackupData(), Cron.Daily) |
连续性任务 | 支付→发货→通知工作流 | BackgroundJob.ContinueJobWith(parentId, () => ShipProduct()) |
🛠️ 三、用法(以 ASP.NET Core 为例)
安装与配置
Install-Package Hangfire.AspNetCore
Install-Package Hangfire.Redis.StackExchange # 若用 Redis 存储
服务注册(Startup.cs
)
public void ConfigureServices(IServiceCollection services) {
services.AddHangfire(config => config
.UseRedisStorage(redisConnectionString) // 或 UseSqlServerStorage()
);
services.AddHangfireServer(); // 启动后台服务
}
启用 Dashboard(Startup.cs
)
public void Configure(IApplicationBuilder app) {
app.UseHangfireDashboard("/jobs"); // 访问路径:http://domain/jobs
// 可选:添加身份验证过滤器
app.UseHangfireServer();
}
创建任务示例:
周期性库存同步:
RecurringJob.AddOrUpdate("stock-sync", () => StockService.Sync(), "0 3 * * *"); // 每天3点执行
业务例子:
我需要每天定时定点往大宽表中插入一次在线库存、当天入库、出库记录
using Hangfire;
using Hangfire.Annotations;
using Hangfire.Dashboard;
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using WmsService.WmsStk.StkBalances;
namespace WmsService
{
/// <summary>
/// 后台工作任务配置
/// </summary>
public static class BackgroundConfigurer
{
/// <summary>
/// 配置服务
/// </summary>
/// <param name="services"></param>
/// <param name="configuration"></param>
public static void Configure(IServiceCollection services, IConfiguration configuration)
{
// Hangfire 任务
services.AddHangfire(config =>
{
config.UseSqlServerStorage(configuration.GetConnectionString("Wms"));
});
}
/// <summary>
/// 请求管道
/// </summary>
/// <param name="app"></param>
/// <param name="configuration"></param>
public static void UseBackground(IApplicationBuilder app, IConfiguration configuration)
{
app.UseHangfireDashboard("/hangfire", new DashboardOptions
{
Authorization = new[]
{
new CustomAuthorizeFilter()
}
});
app.UseHangfireServer();
//// 每天凌晨12点
//Cron.Daily(0, 0)
// 每天中午12点
//Cron.Daily(12, 0)
// 每小时执行一次
//Cron.Hourly()
// 每30分钟执行一次
//Cron.MinuteInterval(30)
//定时调用方法AutoInsertInventoryRecord() 插入库存
RecurringJob.AddOrUpdate<StkBalanceAppService>(x => x.AutoInsertInventoryRecord(), Cron.Daily(0, 1), TimeZoneInfo.Local);
}
}
public class CustomAuthorizeFilter : IDashboardAuthorizationFilter
{
public bool Authorize([NotNull] DashboardContext context)
{
return true;
}
}
}
/// <summary>
/// 定时插入库存记录
/// </summary>
/// <returns></returns>
public virtual async Task AutoInsertInventoryRecord()
{
//定时插入一条
var now = DateTime.Now.Date;
//now = DateTime.ParseExact("2025-02-20", "yyyy-MM-dd", CultureInfo.InvariantCulture);
var startTime = now.AddDays(-1);
//入库总数
var inLists = await _inOrderDapperRepository.QueryInventoryReportByDay(startTime, now);
//出库总数
var outLists = await _outInvoiceDapperRepository.QueryOutboundReportByDay(startTime, now);
//在线库存
var onlineLists = await _outInvoiceDapperRepository.QueryOnlineBalanceReport();
var lists = new List<WmsInventoryRecord>();
foreach (var inDto in inLists)
{
WmsInventoryRecord inventoryRecord = new WmsInventoryRecord();
inventoryRecord.MaterialCode = inDto.MaterialCode;
inventoryRecord.InWareHouseQty = inDto.InQty;
lists.Add(inventoryRecord);
}
foreach (var outDto in outLists)
{
WmsInventoryRecord inventoryRecord = new WmsInventoryRecord();
inventoryRecord.MaterialCode = outDto.MaterialCode;
inventoryRecord.OutBoundQty = outDto.OutQty;
lists.Add(inventoryRecord);
}
foreach (var onlineDto in onlineLists)
{
WmsInventoryRecord inventoryRecord = new WmsInventoryRecord();
inventoryRecord.MaterialCode = onlineDto.MaterialCode;
inventoryRecord.OnlineInventory = onlineDto.OnlineQty;
lists.Add(inventoryRecord);
}
await _wmsInventoryRecordDapperRepository.InsertManyAsync(lists);
}
服务注册
//后台工作任务配置
BackgroundConfigurer.Configure(context.Services, configuration);
BackgroundConfigurer.UseBackground(app, configuration);
⚠️ 四、缺陷与限制
1.不支持秒级定时任务
- 最小调度单位为分钟(依赖 NCrontab 组件),秒级任务需改用 Quartz.NET
2.内存存储(MemoryStorage)的局限性
仅适用于开发和测试环境:
应用重启导致任务丢失。
缺乏线程安全性和持久化保障
3.性能瓶颈
SQL Server 存储依赖轮询机制,高并发场景下可能延迟(可通过 Redis 或 MSMQ 缓解)
4.中文支持问题
部分版本(如 1.8.14)Dashboard 中文显示异常,需降级或手动修复
五、Hangfire与Quartz.Net对比
对比项 | Hangfire | Quartz.NET |
---|---|---|
最小调度粒度 | 分钟级 | 秒级 |
可视化面板 | 内置 Dashboard | 需第三方扩展 |
分布式支持 | 基础多节点 | 原生集群支持 |
适用场景 | 常规后台任务、需可视化监控 | 复杂调度、金融级精准定时 |