【问题标题】:How to prevent hangfire recurring job execution after IIS restart?IIS重启后如何防止hangfire重复执行作业?
【发布时间】:2019-11-05 13:06:53
【问题描述】:

在我们将 Hangfire (v1.7.7) 设置为 always run 后,所有重复作业在我们重新启动 IIS 或回收池后立即执行。这是一种不受欢迎的行为,因为我们安排了每月或每周运行一次的作业,并且每次部署都会使它们运行。

我们在文档或论坛中找不到配置属性来防止这种行为,我们仍在分析 repo,试图找到线索。

我们发现this issuethis discussion 与我们的问题有关。

除了上面提供的链接,它代表了我们为使 Hangfire 始终运行所做的确切配置,这是我们当前的配置:

private BackgroundJobServer _backgroundJobServer;
app.UseHangfireDashboard("/hangfire", new DashboardOptions
{
    Authorization = new[] { new HangfireRestrictiveAuthorizationFilter() },
    StatsPollingInterval = UM_MINUTO_EM_MILISEGUNDOS * 10
});

GlobalConfiguration.Configuration.UseSqlServerStorage("Implanta", new SqlServerStorageOptions 
{ 
    CommandBatchMaxTimeout = TimeSpan.FromMinutes(5), 
    SchemaName = "HangFireSiscaf",
    SlidingInvisibilityTimeout = null
});

_backgroundJobServer = new BackgroundJobServer(new BackgroundJobServerOptions()
{
    Queues = new[] { "siscaf" },
    ServerName = "SISCAF.NET",
    WorkerCount = 5
});

这就是我们通常安排重复作业的方式(一些方法名称和字符串写在 pt-BR 中):

RecurringJob.AddOrUpdate<ColetaMensalCFATask>(
    ColetaMensalCFATask.Key,
    x => x.ExecuteTask(),
    Cron.Monthly(diaAgendamentoMensalColetaCFA, timeSpanHoraAgendamentoMensalColetaCFA.Minutes),
    TimeZoneInfo.Local,
    "siscaf");

更新 1: 通过删除作业执行历史并将每个作业添加为新作业,我们能够重现所需的行为。虽然不是一个令人满意的解决方案。

【问题讨论】:

    标签: c# .net hangfire


    【解决方案1】:

    当您需要更改 Hangfire 中的行为时,您必须写一个 JobFilter

    此类可以实现各种接口,但在您的情况下,如果应该创建作业,您可能可以实现 IClientFilter 并签入 OnCreating(),或者如果应该创建作业,则实现 IElectStateFilter 并签入 OnStateElection()入队。

    请注意,已经有许多其他作业过滤器,它们按照定义的顺序执行,由Order 属性中的整数值给出。为确保您的过滤器是最后一个过滤器,请将其设置为 int.MaxValue

    要将此作业过滤器应用于您的作业,您可以将属性添加到方法或类,或者如果应该为每个作业执行它,您可以在启动时通过GlobalJobFilters.Filters.Add() 将实例添加到全局列表中。

    如果您在其中一个过滤器方法中,它们每个都有一个context 属性,可以通过Storage 属性访问监控API:

    var monitor = context.Storage.GetMonitoringApi();
    var jobDetails = monitor.JobDetails(context.BackgroundJob.Id);
    
    foreach (var kvp in jobDetails.Properties)
    {
        Trace.WriteLine($"{kvp.Key => kvp.Value}");
    }
    
    foreach (var entry in jobDetails.History.OrderBy(e => e.CreatedAt))
    {
        Trace.WriteLine($"{entry.StateName} ({entry.CreatedAt}): {Reason}");
    
        foreach (var kvp in entry.Data)
        {
            Trace.WriteLine($"   {kvp.Key} => {kvp.Value}");
        }
    }
    

    目前我无法在新创建的重复作业的情况下检查它们包含的内容,但如果您使用调试器查看它,您应该会找到一些关于重复作业的信息。检查这一点的一种简单方法是查看 Hangfire Dashboard。如果您打开作业详细信息页面并找到任何链接到重复作业的内容,则可以通过监控 API 获得,因为所有 Hangfire 网页都使用此 API 来获取其内容。

    稍微深入一点,找到了通过给定工作 ID 从重复工作中检索所有信息的要点:

    var monitor = context.Storage.GetMonitoringApi();
    var jobDetails = monitor.JobDetails(context.BackgroundJob.Id);
    
    if(jobDetails.Properties.TryGetValue("RecurringJobId", out string recurringId))
    {
        var values = context.Connection.GetAllEntriesFromHash($"recurring-job:{recurringId}");
    
        foreach (var kvp in values)
        {
            Trace.WriteLine($"{kvp.Key} => {kvp.Value}");
        }
    }
    

    此外,重复作业的 Enqueued 状态始终将文本 Triggered by recurring job scheduler 作为原因,它可以用作这样的过滤器:

        public void OnStateElection(ElectStateContext context)
        {
            switch (context.CandidateState)
            {
                case EnqueuedState enqueued when enqueued.Reason == "Triggered by recurring job scheduler":
                    Trace.WriteLine($"Was triggered by job scheduler.");
    
                    // Skip all jobs of job scheduler
                    context.CandidateState = new SucceededState(null, 0, 0);
                    break;
            }
        }
    

    【讨论】:

    • 我们尝试实现一个类似于the one commented in the issue的JobFilter,当我们的作业被安排时Hangfire立即触发它们并且属性context.InitialStatenull,这意味着我们无法确定原因一个作业被触发,因此无法取消它的执行。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-01-08
    • 2021-06-04
    • 1970-01-01
    • 2019-11-22
    • 2017-03-09
    • 1970-01-01
    • 2015-08-27
    相关资源
    最近更新 更多