Hangfire - 防止同一作业的多个被排队

问题描述 投票:0回答:9

场景:

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

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

现在有三个 Job 1's 正在同时处理 - 我不想要这样。


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

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

c# asp.net hangfire
9个回答
20
投票

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

[DisableConcurrentExecution(timeoutInSeconds: 10 * 60)]
public void Job1()
{
    // Metohd body
}

9
投票

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

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)
        //{

        //}
    }
}

希望有帮助,谢谢!


7
投票

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

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


7
投票

听起来您可能会对以下内容感兴趣: https://discuss.hangfire.io/t/job-reentrancy-avoidance-proposal/607/8

讨论的是跳过将与已运行的作业同时执行的作业。


3
投票

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

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.Arguments) == string.Join(".", filterContext.Job.Arguments)) > 0)
        {
            filterContext.Canceled = true;
        }
    }

    public void OnPerformed(PerformedContext filterContext) { }

    void IClientFilter.OnCreated(CreatedContext filterContext) { }

    void IServerFilter.OnPerforming(PerformingContext filterContext) { }
}

用途:

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

}
  1. 或通过单一工作
[PreventConcurrentExecutionJobFilter]
public class MyJob { 

}

2
投票

我在 RecurringJob 中使用了 DisableConcurrentExecution 属性,但它对我不起作用。

我的错误是我在我的方法上使用它,而不是在我的界面上。

[DisableConcurrentExecution(timeoutInSeconds: 10 * 60)]
Task SyncAllMyDataAsync();



RecurringJob.AddOrUpdate<IMySyncJobs>("Sync my data", x => x.SyncAllMyDataAsync(), "0 0 * * * *");

1
投票

是的。可以如下:

            RecurringJob.AddOrUpdate(Environment.MachineName, () => MyJob(Environment.MachineName), Cron.HourInterval(2));

MyJob 应该这样定义:

    public void MyJob(string taskId)
    {
        if (!taskId.Equals(Environment.MachineName))
        {
            return;
        }
        //Do whatever you job should do.
    }

0
投票

就我而言,我有许多并行运行的方法,但我不能多次运行相同的方法。

使用本主题中的解决方案,我刚刚编辑了查询。如果处理列表中已有一个与实际方法同名的方法,则取消执行。

我相信这可以解决很多案例。

  1. 创建这个类:
    using Hangfire.Client;
    using Hangfire.Common;
    using Hangfire.Server;
    using Hangfire;
    
    public class PreventConcurrentExecutionJobFilter : JobFilterAttribute, IClientFilter, IServerFilter
    {
        public void OnCreating(CreatingContext filterContext)
        {
            var jobs = JobStorage.Current.GetMonitoringApi().ProcessingJobs(0, 100);
    
            var methodAlreadyProcessing = jobs.Any(x => x.Value.Job.Method.Name == filterContext.Job.Method.Name);
    
            if (methodAlreadyProcessing)
            {
                Console.WriteLine($"{DateTime.Now:HH:mm:ss} - Job {filterContext.Job.Method.Name} cancelled why was already exists in processing list!");
                filterContext.Canceled = true;
            }
        }
    
        public void OnPerformed(PerformedContext filterContext) { }
    
        void IClientFilter.OnCreated(CreatedContext filterContext) { }
    
        void IServerFilter.OnPerforming(PerformingContext filterContext) { }
    }

  1. 将注释放入您的方法中:
    [PreventConcurrentExecutionJobFilter]
    public async Task MyTopTask()
    {
      ...
    }

-4
投票

如果你想放弃尝试运行某件事两次(如果它已经在运行),你总是可以这样做(注意没有应用属性):

    private static bool _isRunningUpdateOrders;
    public void UpdateOrders()
    {
        try
        {
            if (_isRunningUpdateOrders)
            {
                return; 
            }

            _isRunningUpdateOrders = true;

            // Logic...

        }
        finally 
        {
            _ isRunningUpdateOrders = false;
        }
   }

编辑:请仅使用类似的方法作为快速解决方案,例如如果您刚刚发现有问题并且仍在评估更好的解决方案:-)或者如果您很懒并且只想“有点”解决问题;-)

最新问题
© www.soinside.com 2019 - 2025. All rights reserved.