C# HangFire的使用
🔍 一、含义
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 | 需第三方扩展 |
分布式支持 | 基础多节点 | 原生集群支持 |
适用场景 | 常规后台任务、需可视化监控 | 复杂调度、金融级精准定时 |