You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Azure WebJob部署Hangfire多实例重复执行同一作业求助

Hangfire 1.6.21 Azure WebJob多实例重复执行作业的临时修复方案

问题根源

Hangfire 1.6.x版本基于SQL Server存储时,分布式锁机制存在局限性:当Azure实例缩容、异常宕机或多实例并发拾取作业时,锁的释放和竞争处理逻辑不够健壮,导致同一作业被多个实例标记为In Progress并执行。

临时修复方案

1. 调整Hangfire SQL存储的锁与超时参数

修改Hangfire初始化代码,延长分布式锁超时、作业不可见性超时,降低队列轮询频率,减少并发冲突概率:

var connectionString = "Your_Azure_SQL_Connection_String";
var storage = new SqlServerStorage(connectionString, new SqlServerStorageOptions
{
    // 延长分布式锁超时,避免实例短暂不可用时锁提前释放
    DistributedLockTimeout = TimeSpan.FromSeconds(30),
    // 延长作业不可见性超时,减少其他实例拾取同一作业的机会
    SlidingInvisibilityTimeout = TimeSpan.FromSeconds(15),
    // 降低队列轮询间隔,减少并发查询冲突
    QueuePollInterval = TimeSpan.FromSeconds(5),
    CommandBatchMaxTimeout = TimeSpan.FromSeconds(10)
});

GlobalConfiguration.Configuration.UseStorage(storage);

2. 自定义过滤器拦截重复执行的作业

实现作业执行前的状态检查,当同一JobId存在多条In Progress记录时,取消当前执行:

public class PreventDuplicateExecutionFilter : IServerFilter
{
    private readonly string _connectionString;

    public PreventDuplicateExecutionFilter(string connectionString)
    {
        _connectionString = connectionString;
    }

    public void OnPerforming(PerformingContext context)
    {
        var jobId = context.BackgroundJob.Id;
        using (var conn = new SqlConnection(_connectionString))
        {
            conn.Open();
            // 注意不要用NOLOCK,避免读取未提交的脏数据
            var inProgressCount = conn.QueryFirstOrDefault<int>(
                @"SELECT COUNT(*) FROM [HangFire].[State] 
                  WHERE JobId = @JobId AND Name = 'InProgress'",
                new { JobId = jobId });

            if (inProgressCount > 1)
            {
                context.Canceled = true;
                // 可添加日志记录,便于排查
                // Log.Warn($"Job {jobId} is already in progress, execution canceled.");
            }
        }
    }

    public void OnPerformed(PerformedContext context) { }
}

注册过滤器到Hangfire全局配置:

GlobalJobFilters.Filters.Add(new PreventDuplicateExecutionFilter(connectionString));

3. 优化Azure WebJob实例配置

  • 优雅关闭配置:在Azure Portal的WebApp配置中,将"优雅关闭时间"设置为最大值120秒,确保实例缩容时Hangfire有足够时间完成当前作业或释放锁。
  • 启用实例预热:开启WebApp的"实例预热"功能,避免新实例启动后立即批量拾取作业,减少并发冲突。
  • 始终开启:确保WebApp设置为"始终开启",避免实例休眠导致锁异常释放。

4. 定期清理异常的In Progress状态

创建定时任务(可用Hangfire定时作业或Azure WebJob定时触发),定期清理超时的In Progress状态记录,避免残留状态导致重复执行:

-- 清理超过30分钟的In Progress状态(根据实际作业时长调整)
DELETE FROM [HangFire].[State] 
WHERE Name = 'InProgress' 
  AND CreatedAt < DATEADD(MINUTE, -30, GETUTCDATE())

总结

以上方案无需升级Hangfire版本,可快速部署实施,有效降低作业重复执行的概率。长期来看,升级到Hangfire最新版本(2.x及以上)能从根本上解决分布式锁和并发处理的缺陷。

内容的提问来源于stack exchange,提问作者Sagar

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.13 06:22:20