Hangfire定时部署(.NET 8 + SQL Server)
一、创建 .NET 8 项目
首先创建一个 .NET 8 项目(ASP.NET Core Web 应用或控制台应用),这里以 ASP.NET Core Web API 为例。
二、安装必要的 NuGet 包
核心包(适用于 ASP.NET Core)
Install-Package Hangfire.AspNetCore存储后端(选择一个)
Install-Package Hangfire.SqlServer # SQL Server
Install-Package Hangfire.Redis.StackExchange # Redis
Install-Package Hangfire.PostgreSql # PostgreSQL其他拓展
Install-Package Microsoft.Data.SqlClient
Install-Package Swashbuckle.AspNetCore
三、完整配置步骤(.NET 8 + SQL Server)
- 配置连接字符串
在 appsettings.json 中添加数据库连接:
需先创建一个数据库
Windows本机连接
{"ConnectionStrings": {"HangfireConnection": "Server=localhost;Database=HangfireNet8;Integrated Security=True;TrustServerCertificate=True"}
}IP账号密码连接"ConnectionStrings": {"HangfireConnection": "Server=127.0.0.1;Database=HangfireDB;User Id=sa;Password=123456;TrustServerCertificate=True;Encrypt=False"}
- 配置 Program.cs
using Hangfire;
using Hangfire.Dashboard;
using Hangfire.SqlServer;
using Microsoft.Extensions.Logging;
using System.Net;var builder = WebApplication.CreateBuilder(args);// 添加 Hangfire 服务
builder.Services.AddHangfire(configuration => configuration.SetDataCompatibilityLevel(CompatibilityLevel.Version_180).UseSimpleAssemblyNameTypeSerializer().UseRecommendedSerializerSettings()// 配置 SQL Server 存储.UseSqlServerStorage(builder.Configuration.GetConnectionString("HangfireConnection"), new SqlServerStorageOptions{CommandBatchMaxTimeout = TimeSpan.FromMinutes(5),SlidingInvisibilityTimeout = TimeSpan.FromMinutes(5),QueuePollInterval = TimeSpan.Zero,UseRecommendedIsolationLevel = true,DisableGlobalLocks = true,EnableHeavyMigrations = true})
);// 添加 Hangfire 服务器
builder.Services.AddHangfireServer(options =>
{options.WorkerCount = Math.Max(Environment.ProcessorCount * 5, 10);options.Queues = new[] { "high", "medium", "low" };options.ShutdownTimeout = TimeSpan.FromMinutes(1);
});// 添加控制器和Swagger服务
builder.Services.AddControllers();
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();// 注册应用服务
builder.Services.AddScoped<ITaskService, TaskService>();var app = builder.Build();
var isDevelopment = app.Environment.IsDevelopment();// 配置HTTP请求管道
if (isDevelopment)
{app.UseSwagger();app.UseSwaggerUI();
}app.UseHttpsRedirection();
app.UseRouting();
app.UseAuthorization();// 启用 Hangfire 仪表板
app.UseHangfireDashboard("/hangfire", new DashboardOptions
{Authorization = new[] { new HangfireAuthFilter(isDevelopment) }
});// 定义任务
using (var scope = app.Services.CreateScope())
{var services = scope.ServiceProvider;try{var taskService = services.GetRequiredService<ITaskService>();// 1. 即时任务BackgroundJob.Enqueue(() => taskService.ProcessData("initial"));// 2. 延迟任务(30分钟后执行)BackgroundJob.Schedule(() => taskService.SendNotification("delayed"),TimeSpan.FromMinutes(1));// 3. 循环任务(每小时执行一次)RecurringJob.AddOrUpdate("hourly-cleanup",() => taskService.CleanupResources(),Cron.Hourly,TimeZoneInfo.Local);// 4. 延续任务var parentJobId = BackgroundJob.Enqueue(() => taskService.ProcessOrder(1001));BackgroundJob.ContinueWith(parentJobId,() => taskService.CompleteOrder(1001));}catch (Exception ex){var logger = services.GetRequiredService<ILogger<Program>>();logger.LogError(ex, "注册Hangfire任务时发生错误");}
}app.MapControllers();app.Run();// 自定义授权过滤器 - 修复空值问题
public class HangfireAuthFilter : IDashboardAuthorizationFilter
{private readonly bool _isDevelopment;public HangfireAuthFilter(bool isDevelopment){_isDevelopment = isDevelopment;}public bool Authorize(DashboardContext context){// 确保上下文不为空if (context == null)return false;// 获取HTTP上下文并检查var httpContext = context.GetHttpContext();if (httpContext == null)return false;// 开发环境直接允许访问if (_isDevelopment)return true;// 生产环境检查是否为本地访问var localIpAddress = httpContext.Connection.LocalIpAddress;if (localIpAddress == null)return false;// 明确检查是否为回环地址(IPv4和IPv6)return IPAddress.IsLoopback(localIpAddress);}
}// 任务服务接口
public interface ITaskService
{void ProcessData(string data);void SendNotification(string message);void CleanupResources();void ProcessOrder(int orderId);void CompleteOrder(int orderId);
}// 任务服务实现
public class TaskService : ITaskService
{private readonly ILogger<TaskService> _logger;// 确保日志服务不为空public TaskService(ILogger<TaskService> logger){_logger = logger ?? throw new ArgumentNullException(nameof(logger));}public void ProcessData(string data){if (string.IsNullOrEmpty(data))throw new ArgumentException("数据不能为空", nameof(data));_logger.LogInformation("处理数据: {Data}", data);}public void SendNotification(string message){if (string.IsNullOrEmpty(message))throw new ArgumentException("消息不能为空", nameof(message));_logger.LogInformation("发送通知: {Message}", message);}public void CleanupResources(){_logger.LogInformation("清理资源于 {Time}", DateTime.Now);}public void ProcessOrder(int orderId){if (orderId <= 0)throw new ArgumentOutOfRangeException(nameof(orderId), "订单ID必须大于0");_logger.LogInformation("处理订单: {OrderId}", orderId);}public void CompleteOrder(int orderId){if (orderId <= 0)throw new ArgumentOutOfRangeException(nameof(orderId), "订单ID必须大于0");_logger.LogInformation("订单完成: {OrderId}", orderId);}
}
四、数据库初始化
首次运行应用时,Hangfire 会自动在指定的数据库中创建所需的表结构(约 15 张表),无需手动创建。
五、访问 Hangfire 仪表板
启动应用后,访问 https://localhost:端口/hangfire 即可打开管理界面,在这里可以:
查看所有任务的执行状态
手动触发、暂停或删除循环任务
重试失败的任务
监控服务器状态和性能