【发布时间】:2017-07-18 10:37:28
【问题描述】:
场景:
作业 1 计划每 5 分钟运行一次,大约需要 1 分钟才能完成。
大量工作堆积起来,作业 1 需要 15 分钟才能运行。
现在同时处理三个 Job 1 - 我不希望这样。
如果 Job 1 已经存在,如何防止它再次添加到队列中?
是否有 Hangfire 设置,还是我需要手动轮询作业状态?
【问题讨论】:
作业 1 计划每 5 分钟运行一次,大约需要 1 分钟才能完成。
大量工作堆积起来,作业 1 需要 15 分钟才能运行。
现在同时处理三个 Job 1 - 我不希望这样。
是否有 Hangfire 设置,还是我需要手动轮询作业状态?
【问题讨论】:
您可以使用DisableConcurrentExecution 属性来防止同时执行多个方法。只需将此属性放在您的方法之上 -
[DisableConcurrentExecution(timeoutInSeconds: 10 * 60)]
public void Job1()
{
// Metohd body
}
【讨论】:
有点晚了,但我正在使用这个类来防止重复的作业同时运行
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)
//{
//}
}
}
希望对您有所帮助,谢谢!
【讨论】:
您可能对以下内容感兴趣: https://discuss.hangfire.io/t/job-reentrancy-avoidance-proposal/607/8
讨论是关于跳过将同时执行的作业到已经运行的作业。
【讨论】:
有一个名为 DisableConcurrentExecution 的属性,可以防止 2 个相同类型的作业同时运行。
不过,在您的情况下,最好检查任务是否运行并相应地跳过。
【讨论】:
是的。可能如下:
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.
}
【讨论】:
如果你想放弃已经运行两次的尝试,你总是可以这样做(注意没有应用任何属性):
private static bool _isRunningUpdateOrders;
public void UpdateOrders()
{
try
{
if (_isRunningUpdateOrders)
{
return;
}
_isRunningUpdateOrders = true;
// Logic...
}
finally
{
_ isRunningUpdateOrders = false;
}
}
编辑:请仅使用类似的方法作为快速修复,例如,如果您刚刚发现问题并且仍在评估更好的解决方案 :-) 或者如果您很懒,只想“解决问题;-)
【讨论】:
try 语句之前使用 if (Interlocked.Exchange(ref _isRunningUpdateOrders, 1) == 1) return; 并在 finally 部分内使用 Interlocked.Exchange(ref _isRunningUpdateOrders, 0);。顺便说一句,Hangfire 文档还提到,有时人们可能更喜欢自己的锁定机制——在单个服务器处理作业的情况下。