如何禁用长时间运行的hangfire作业的并发执行



背景:我正在使用Hangfire with.net web应用程序运行定期作业,该作业计划在每月的第三个星期六进行。但根据需要,客户端可以从hangfire仪表板手动触发该作业,该作业是长时间运行的作业,可能需要几个小时才能完成。

问题:

  1. 手动触发此作业时,有时会自动调用2次,不知道为什么。

    有人知道这个问题吗?

  2. 为了解决这个问题,我看到许多人建议禁用并发执行属性,因为该属性要求超时秒,我怀疑它是否有效长期工作。

    如果一个作业已经启动,有什么方法可以避免调用长时间运行的作业吗?

为了最终解决这个问题,我做了以下

这些属性被添加到我的界面

[PingUrlToKeepAlive]
[SkipWhenPreviousJobIsRunning]
[DisableConcurrentExecution(10)]
[Queue("{0}")]

PingUrl是一个创建的属性,用于阻止IIS进程在20分钟后在任一服务器上关闭,与此修复无关,我只是想提一下

根据hangfire的说法,排队是现在推荐的方式。

DisableConcurrentExecution是我认为只需要的属性,但您也需要下面的属性。

SkipWhenPreviousJobIsRunning是一个新属性,看起来像这个

public class SkipWhenPreviousJobIsRunningAttribute: JobFilterAttribute, IClientFilter, IApplyStateFilter
{
public void OnCreating(CreatingContext context)
{
var connection = context.Connection as JobStorageConnection;
// We can't handle old storages
if (connection == null) return;
// We should run this filter only for background jobs based on 
// recurring ones
if (!context.Parameters.ContainsKey("RecurringJobId")) return;
var recurringJobId = context.Parameters["RecurringJobId"] as string;
// RecurringJobId is malformed. This should not happen, but anyway.
if (string.IsNullOrWhiteSpace(recurringJobId)) return;
var running = connection.GetValueFromHash($"recurring-job:{recurringJobId}", "Running");
if ("yes".Equals(running, StringComparison.OrdinalIgnoreCase))
{
context.Canceled = true;
}
}
public void OnCreated(CreatedContext filterContext)
{
}
public void OnStateApplied(ApplyStateContext context, IWriteOnlyTransaction transaction)
{
if (context.NewState is EnqueuedState)
{
var recurringJobId = SerializationHelper.Deserialize<string>(context.Connection.GetJobParameter(context.BackgroundJob.Id, "RecurringJobId"));
if (string.IsNullOrWhiteSpace(recurringJobId)) return;
transaction.SetRangeInHash(
$"recurring-job:{recurringJobId}",
new[] { new KeyValuePair<string, string>("Running", "yes") });
}
else if (context.NewState.IsFinal /* || context.NewState is FailedState*/)
{
var recurringJobId = SerializationHelper.Deserialize<string>(context.Connection.GetJobParameter(context.BackgroundJob.Id, "RecurringJobId"));
if (string.IsNullOrWhiteSpace(recurringJobId)) return;
transaction.SetRangeInHash(
$"recurring-job:{recurringJobId}",
new[] { new KeyValuePair<string, string>("Running", "no") });
}
}
public void OnStateUnapplied(ApplyStateContext context, IWriteOnlyTransaction transaction)
{
}
}

基本上,这会检查作业是否已经运行,如果已经运行,则取消它。我们现在在两台服务器上同时运行作业没有问题。

以上适用于递归作业,但您可以很容易地将其更改为适用于所有作业。

最新更新