Hangfire - 防止多个相同的工作被排队

Jay*_*waa 13 c# asp.net hangfire

设想:

作业 1计划每 5 分钟运行一次,大约需要 1 分钟才能完成。

大量工作堆积如山,作业 1需要 15 分钟才能运行。

现在同时处理三个作业 1 - 我不想要这个。


如果作业 1已经存在,如何防止它再次添加到队列中?

是否有 Hangfire 设置,或者我是否需要手动轮询作业状态?

Yog*_*ogi 13

您可以使用DisableConcurrentExecution属性来防止同时执行多个方法。只需将此属性放在您的方法之上 -

[DisableConcurrentExecution(timeoutInSeconds: 10 * 60)]
public void Job1()
{
    // Metohd body
}
Run Code Online (Sandbox Code Playgroud)

  • 请注意,Hangfire 可能会以这种方式导致数据库连接池泄漏:https://codewithstyle.info/real-bug-story-long-running-jobs-hangfire/ (3认同)

The*_*aut 7

有一个名为“DisableConcurrentExecution”的属性,可防止 2 个相同类型的作业同时运行。

不过,就您的情况而言,最好检查任务是否运行并相应地跳过。

  • 据我了解,这会阻止它们同时执行,但它们仍然会排队运行。这是朝着正确方向迈出的一步,但理想情况下他们根本不会排队。感谢你的回答! (2认同)

小智 7

有点晚了,但我正在使用这个类来防止重复的作业同时运行

public class SkipConcurrentExecutionAttribute : JobFilterAttribute, IServerFilter, IElectStateFilter
{
    private readonly int _timeoutSeconds;
    private const string DistributedLock = "DistributedLock";

    public SkipConcurrentExecutionAttribute(int timeOutSeconds)
    {
        if (timeOutSeconds < 0) throw new ArgumentException("Timeout argument value should be greater that zero.");
        this._timeoutSeconds = timeOutSeconds;
    }

    public void OnPerformed(PerformedContext filterContext)
    {
        if (!filterContext.Items.ContainsKey(DistributedLock))
            throw new InvalidOperationException("Can not release a distributed lock: it was not acquired.");

        var distributedLock = (IDisposable)filterContext.Items[DistributedLock];
        distributedLock?.Dispose();
    }



    public void OnPerforming(PerformingContext filterContext)
    {
        filterContext.WriteLine("Job Started");

        var resource = String.Format(
                           "{0}.{1}",
                          filterContext.BackgroundJob.Job.Type.FullName,
                          filterContext.BackgroundJob.Job.Method.Name);

        var timeOut = TimeSpan.FromSeconds(_timeoutSeconds);

        filterContext.WriteLine($"Waiting for running jobs to complete. (timeout: { _timeoutSeconds })");

        try
        {
            var distributedLock = filterContext.Connection.AcquireDistributedLock(resource, timeOut);
            filterContext.Items[DistributedLock] = distributedLock;
        }
        catch (Exception ex)
        {
            filterContext.WriteLine(ex);
            filterContext.WriteLine("Another job is already running, aborted.");
            filterContext.Canceled = true; 
        }

    }

    public void OnStateElection(ElectStateContext context)
    {
        //if (context.CandidateState as FailedState != null)
        //{

        //}
    }
}
Run Code Online (Sandbox Code Playgroud)

希望有帮助,谢谢!


Phi*_*ohl 6

听起来这可能是您可能感兴趣的内容:https : //discuss.hangfire.io/t/job-reentrancy-avoidance-proposal/607/8

讨论是关于跳过将并发执行的作业到已经运行的作业。


小智 5

我解决了这个问题。我希望这个对你有用

public class PreventConcurrentExecutionJobFilter : JobFilterAttribute, IClientFilter, IServerFilter
{
    public void OnCreating(CreatingContext filterContext)
    {
        var jobs = JobStorage.Current.GetMonitoringApi().ProcessingJobs(0, 100);
        if (jobs.Count(x => x.Value.Job.Type == filterContext.Job.Type && string.Join(".", x.Value.Job.Args) == string.Join(".", filterContext.Job.Args)) > 0)
        {
            filterContext.Canceled = true;
        }
    }

    public void OnPerformed(PerformedContext filterContext) { }

    void IClientFilter.OnCreated(CreatedContext filterContext) { }

    void IServerFilter.OnPerforming(PerformingContext filterContext) { }
}
Run Code Online (Sandbox Code Playgroud)

用途:

  1. 添加到全局过滤器
GlobalJobFilters.Filters.Add(new PreventConcurrentExecutionJobFilter());
Run Code Online (Sandbox Code Playgroud)
  1. 或通过抽象基作业类
[PreventConcurrentExecutionJobFilter]
public abstract class IHangfireJob { 

}
Run Code Online (Sandbox Code Playgroud)
  1. 或通过单一工作
[PreventConcurrentExecutionJobFilter]
public class MyJob { 

}
Run Code Online (Sandbox Code Playgroud)