@IT·互联网

C#.NET 定时任务与队列利器:Hangfire 完整教程

2025-09-17  本文已影响0人  青枫教学

简介

组件 职责 部署方式
客户端 创建任务(立即、延迟、周期性) 可嵌入 WebAPI/控制台等应用
服务端 从存储拉取任务并执行,支持多线程处理 可宿主于 IIS/Windows 服务/控制台程序
持久化存储 保存任务信息(方法名、参数、状态) SQL Server/MySQL/Redis/MongoDB 等

安装与基础配置

安装 NuGet 包

# 核心包
Install-Package Hangfire.Core
# ASP.NET Core 集成
Install-Package Hangfire.AspNetCore
Install-Package Hangfire.SqlServer    # 或者 Hangfire.Redis、Hangfire.PostgreSql 等

在 ASP.NET Core 中配置(Program.cs)

var builder = WebApplication.CreateBuilder(args);
// 注册 Hangfire 服务
builder.Services.AddHangfire(cfg => cfg
    .UseSimpleAssemblyNameTypeSerializer()
    .UseRecommendedSerializerSettings()
    .UseSqlServerStorage(builder.Configuration.GetConnectionString("Hangfire"), new SqlServerStorageOptions {
        CommandBatchMaxTimeout = TimeSpan.FromMinutes(5),
        SlidingInvisibilityTimeout = TimeSpan.FromMinutes(5),
        QueuePollInterval = TimeSpan.Zero,
        UseRecommendedIsolationLevel = true,
        UsePageLocksOnDequeue = true,
        DisableGlobalLocks = true
    }));
// 添加后台服务器
builder.Services.AddHangfireServer();  

var app = builder.Build();
// 挂载 Dashboard(可选授权)
app.UseHangfireDashboard("/hangfire");
app.MapControllers();
app.Run();

持久化选项

核心用法

即时执行(Fire‑and‑Forget)

// 任意方法入队,立即返回
BackgroundJob.Enqueue(() => Console.WriteLine("Hello, Hangfire!"));

带参数的任务

BackgroundJob.Enqueue<EmailService>(
    x => x.SendWelcomeEmail("user@example.com"));

延迟执行

// 延迟 5 分钟后执行
BackgroundJob.Schedule(() => SendEmail(userId), TimeSpan.FromMinutes(5));

定时任务(Recurring Jobs)

// 每天凌晨 2 点执行的任务
RecurringJob.AddOrUpdate(() => CleanupDatabase(), Cron.Daily(2, 0));

// 使用自定义 Cron 表达式
RecurringJob.AddOrUpdate("daily-report", () => GenerateDailyReport(), "0 2 * * *");

继续任务(Continuation)

var parentId = BackgroundJob.Enqueue(() => Step1());
BackgroundJob.ContinueWith(parentId, () => Step2());

自定义任务方法

// 定义可被 Hangfire 调用的方法
public static class EmailService
{
    public static void SendEmail(string recipient)
    {
        // 发送邮件的逻辑
        Console.WriteLine($"Sending email to {recipient}");
    }
}

// 在控制器中使用
[ApiController]
[Route("[controller]")]
public class JobController : ControllerBase
{
    [HttpPost]
    public IActionResult CreateJob()
    {
        // 入队一个任务
        BackgroundJob.Enqueue(() => EmailService.SendEmail("user@example.com"));
        return Ok();
    }
}

批量任务(Batch,需额外包 Hangfire.Pro 或社区插件)

var batchId = BatchJob.StartNew(batch => {
    batch.Enqueue(() => TaskA());
    batch.Enqueue(() => TaskB());
});

// 批处理完成后执行
Batch.ContinueBatchWith(batchId, 
    () => SendNotification("Batch completed!"));

存储结构与特性

核心表结构(以 SQL Server 为例)

仪表盘与监控

Dashboard UI

实时监控

GlobalJobFilters.Filters.Add(new ProlongExpirationTimeAttribute());

高级与扩展

筛选器(Filters)

public class LoggingAttribute : JobFilterAttribute, IServerFilter {
    public void OnPerforming(PerformingContext filterContext) { /* 前置 */ }
    public void OnPerformed(PerformedContext filterContext) { /* 后置 */ }
}
// 创建自定义过滤器
public class AutomaticRetryAttribute : JobFilterAttribute, IClientFilter, IServerFilter
{
    public int Attempts { get; set; } = 3;

    public void OnCreating(CreatingContext context)
    {
        context.SetJobParameter("RetryCount", Attempts);
    }

    public void OnCreated(CreatedContext context) { }

    public void OnPerforming(PerformingContext context) { }

    public void OnPerformed(PerformedContext context)
    {
        if (context.Exception != null)
        {
            var retryCount = context.GetJobParameter<int>("RetryCount");
            if (retryCount > 0)
            {
                context.Requeue();
                context.SetJobParameter("RetryCount", retryCount - 1);
            }
        }
    }
}

// 全局注册过滤器
GlobalConfiguration.Configuration.UseFilter(new AutomaticRetryAttribute { Attempts = 5 });
// 全局
GlobalJobFilters.Filters.Add(new AutomaticRetryAttribute { Attempts = 5, DelaysInSeconds = new[]{10,60,300} });
// 单个任务
[AutomaticRetry(Attempts = 3, OnAttemptsExceeded = AttemptsExceededAction.Delete)]
public void DoWork() { /* ... */ }

配置工作进程

services.AddHangfireServer(options =>
{
    options.WorkerCount = Environment.ProcessorCount * 2; // 设置工作进程数量
    options.Queues = new[] { "critical", "default", "low" }; // 配置队列优先级
});

仪表盘扩展

// 自定义仪表盘页面
app.UseHangfireDashboard("/hangfire", new DashboardOptions
{
    AppPath = "/", // 返回主应用的链接
    DashboardTitle = "任务调度中心",
    Authorization = new[] { new HangfireDashboardAuthorizationFilter() },
    DisplayStorageConnectionString = false,
    IgnoreAntiforgeryToken = true,
    
    // 添加自定义菜单
    AdditionalDashboardUrls = new []
    {
        new DashboardUrl
        {
            Url = "/custom",
            Title = "自定义监控",
            Type = DashboardUrlType.Link
        }
    }
});

集成依赖注入

// 在控制器中注入 IBackgroundJobClient
public class OrderController : ControllerBase
{
    private readonly IBackgroundJobClient _backgroundJobClient;

    public OrderController(IBackgroundJobClient backgroundJobClient)
    {
        _backgroundJobClient = backgroundJobClient;
    }

    [HttpPost]
    public IActionResult CreateOrder(Order order)
    {
        // 创建订单
        _backgroundJobClient.Enqueue<OrderService>(x => x.ProcessOrder(order.Id));
        return Ok();
    }
}

// 使用依赖注入的服务
public class OrderService
{
    private readonly ILogger<OrderService> _logger;
    private readonly IOrderRepository _orderRepository;

    public OrderService(ILogger<OrderService> logger, IOrderRepository orderRepository)
    {
        _logger = logger;
        _orderRepository = orderRepository;
    }

    public void ProcessOrder(int orderId)
    {
        _logger.LogInformation($"Processing order {orderId}");
        var order = _orderRepository.GetById(orderId);
        // 处理订单逻辑
    }
}

多队列管理

// 配置作业服务器处理特定队列
services.AddHangfireServer(options => {
    options.Queues = new[] { "critical", "default", "low" };
});

// 提交任务到特定队列
BackgroundJob.Enqueue<CriticalService>(
    x => x.ProcessCriticalTask(), 
    "critical"); // 指定队列名称

Redis存储优化

config.UseRedisStorage(redisConnection, new RedisStorageOptions
{
    Prefix = "hangfire:",
    Db = 1,
    InvisibilityTimeout = TimeSpan.FromMinutes(5),
    FetchTimeout = TimeSpan.FromSeconds(15)
});

最佳实践

周期性任务未触发

高并发下性能问题

Hangfire与其他方案对比

特性 Hangfire Quartz.NET Azure Functions
部署方式 集成应用 独立进程/集成 无服务器
持久化存储 多种选择 多种选择 Azure存储
监控界面 内置完善 有限或第三方 Azure门户
扩展性 良好 良好 自动扩展
成本 免费开源 免费开源 按使用付费
学习曲线 中等 较陡峭 中等
适用场景 应用内任务调度 复杂调度系统 云原生应用

总结

Hangfire 核心优势:

典型应用场景:

何时选择Hangfire:

资源与扩展

上一篇 下一篇

猜你喜欢

热点阅读