在 .NET 或 .NET Core 應用中,若需在不依賴 Windows 服務、獨立進程的前提下實現后臺處理,Hangfire 是最成熟、簡單的方案之一 —— 它可直接嵌入現有應用(如?ASP.NET?Core Web 應用),無需額外部署,同時提供任務持久化、監控和重試能力。以下從?核心優勢、快速集成步驟、關鍵用法?三方面,梳理如何用 Hangfire 實現后臺處理:
一、為什么選擇 Hangfire(核心優勢)
- 零額外部署:可直接嵌入 Web 應用、控制臺應用,無需單獨部署 Windows 服務或計劃任務;
- 任務持久化:支持 SQL Server、MySQL、Redis 等存儲,應用重啟后未執行的任務不會丟失;
- 可視化監控:自帶 Dashboard 控制臺,可實時查看任務狀態(成功 / 失敗 / 排隊)、執行日志、重試記錄;
- 多任務類型支持:覆蓋常見后臺場景(一次性任務、定時任務、周期性任務);
- 高可靠性:自動重試失敗任務,支持任務優先級(隊列區分),避免單點故障。
Install-Package Hangfire.AspNetCore
Install-Package Hangfire.MySqlStorage
#region Hangfire與MySQLstring connString = configuration.GetConnectionString("Hangfire")?? throw new ArgumentException("未找到Hangfire連接字符串");context.Services.AddHangfire(config =>{config.SetDataCompatibilityLevel(CompatibilityLevel.Version_180).UseSimpleAssemblyNameTypeSerializer().UseRecommendedSerializerSettings();config.UseStorage(new MySqlStorage(connString, new MySqlStorageOptions()));});context.Services.AddHangfireServer();context.Services.AddTransient<IBackgroundJobClient, BackgroundJobClient>();#endregion#region 定義Queuescontext.Services.AddHangfireServer(options =>{options.Queues = new[] { "maintenance-plan-overtime" };options.WorkerCount = 1;});#endregion
}public override async Task OnApplicationInitializationAsync(
ApplicationInitializationContext context)
{await context.AddBackgroundWorkerAsync<LimitRecordWorker>();
}public override Task OnPostApplicationInitializationAsync(ApplicationInitializationContext context)
{const string NAME = "HangfirePeriodicBackgroundWorkerAdapter<BackgroundJobWorker>.DoWorkAsync";RecurringJob.RemoveIfExists(NAME);return base.OnPostApplicationInitializationAsync(context);
}
如果你想使用 Hangfire Dashboard 來查看和管理后臺任務,你需要在?Configure
?方法中添加相應的中間件:
app.UseHangfireDashboard();
定時任務:
public class LimitRecordWorker : HangfireBackgroundWorkerBase
{private readonly IAbpDistributedLock _distributedLock;private readonly IUnitOfWorkManager _unitOfWorkManager;private readonly IBackgroundJobManager _backgroundJobManager;public LimitRecordWorker(IAbpDistributedLock distributedLock,IUnitOfWorkManager unitOfWorkManager,IConfiguration configuration,IBackgroundJobManager backgroundJobManager){_distributedLock = distributedLock;_unitOfWorkManager = unitOfWorkManager;RecurringJobId = nameof(LimitRecordWorker);_backgroundJobManager = backgroundJobManager;CronExpression = configuration["Crons:LimitRecordCron"];}public override async Task DoWorkAsync(CancellationToken cancellationToken = default){await using (var handle = await _distributedLock.TryAcquireAsync("LimitRecordDistributedLock", TimeSpan.FromSeconds(10))){if (handle != null){using (var uow = LazyServiceProvider.LazyGetRequiredService<IUnitOfWorkManager>().Begin()){Log.Information("LimitRecordWorker" + DateTime.Now);for (int i = 0; i <= 5; i++){MaintenancePlanOverTimeArgs workProcedureLeaderArgs = new MaintenancePlanOverTimeArgs();workProcedureLeaderArgs.Serial = i;await _backgroundJobManager.EnqueueAsync(workProcedureLeaderArgs, delay: TimeSpan.FromHours(workProcedureLeaderArgs.OverTime % 24));}await uow.CompleteAsync();}}}}
}
隊列服務:
[Queue("maintenance-plan-overtime")]public class MaintenancePlanOverTimeJob : AsyncBackgroundJob<MaintenancePlanOverTimeArgs>, ITransientDependency{public MaintenancePlanOverTimeJob(){}public override async Task ExecuteAsync(MaintenancePlanOverTimeArgs args){Log.Information("MaintenancePlanOverTimeJob:" + args.Serial + "--" + DateTime.Now);}}public class MaintenancePlanOverTimeArgs{public int Serial { get; set; }public List<Guid> EmployeeIds { get; set; }public Guid DepartmentId { get; set; }public Guid? WorkProcedureId { get; set; }public Guid? DeviceDetailTypeId { get; set; }public double OverTime { get; set; }}
效果: