C# HangFire的使用

发布于:2025-08-16 ⋅ 阅读:(14) ⋅ 点赞:(0)

🔍 一、含义

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();  // 启动后台服务
}

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

网站公告

今日签到

点亮在社区的每一天
去签到