【问题标题】:How do I get the current attempt number on a background job in Hangfire?如何在 Hangfire 中获取后台作业的当前尝试次数?
【发布时间】:2016-11-17 00:19:48
【问题描述】:

在我的 Hangfire 后台作业的最后一次尝试结束之前,我需要执行一些数据库操作(我需要删除与作业相关的数据库记录)

我当前的工作设置了以下属性:
[AutomaticRetry(Attempts = 5, OnAttemptsExceeded = AttemptsExceededAction.Delete)]

考虑到这一点,我需要确定当前的尝试次数是多少,但我很难从 Google 搜索或 Hangfire.io 文档中找到这方面的任何文档。

【问题讨论】:

    标签: asp.net-mvc scheduled-tasks hangfire


    【解决方案1】:

    (注意!这是 OP 问题的解决方案。它不回答“如何获取当前尝试次数”的问题。如果这是您想要的,请参阅 accepted answer 例如)

    使用作业过滤器和OnStateApplied 回调:

    public class CleanupAfterFailureFilter : JobFilterAttribute, IServerFilter, IApplyStateFilter
    {
        public void OnStateApplied(ApplyStateContext context, IWriteOnlyTransaction transaction)
        {
            try
            {
                var failedState = context.NewState as FailedState;
                if (failedState != null)
                {
                    // Job has finally failed (retry attempts exceeded)
                    // *** DO YOUR CLEANUP HERE ***
                }
            }
            catch (Exception)
            {
                // Unhandled exceptions can cause an endless loop.
                // Therefore, catch and ignore them all.
                // See notes below.
            }
        }
    
        public void OnStateUnapplied(ApplyStateContext context, IWriteOnlyTransaction transaction)
        {
            // Must be implemented, but can be empty.
        }
    }
    

    将过滤器直接添加到工作职能中:

    [CleanupAfterFailureFilter]
    public static void MyJob()
    

    或全局添加:

    GlobalJobFilters.Filters.Add(new CleanupAfterFailureFilter ());
    

    或者像这样:

    var options = new BackgroundJobServerOptions
    {   
        FilterProvider = new JobFilterCollection { new CleanupAfterFailureFilter () };
    };
    
    app.UseHangfireServer(options, storage);
    

    或查看http://docs.hangfire.io/en/latest/extensibility/using-job-filters.html,了解有关作业过滤器的更多信息。

    注意:这是基于接受的答案:https://stackoverflow.com/a/38387512/2279059

    不同之处在于使用OnStateApplied而不是OnStateElection,因此只有在最大重试次数后才会调用过滤器回调。这种方法的一个缺点是不能中断到“失败”的状态转换,但在这种情况下不需要这样做,并且在大多数情况下您只想在作业失败后进行一些清理。

    注意:空的catch 处理程序不好,因为它们可以隐藏错误并使它们难以在生产中调试。这里是必要的,所以回调不会永远被重复调用。您可能希望记录异常以进行调试。还建议降低作业过滤器中出现异常的风险。一种可能性是,而不是就地进行清理工作,而是安排一个新的后台作业,如果原始作业失败,该作业将运行。但请注意不要将过滤器 CleanupAfterFailureFilter 应用于它。不要全局注册它,或者给它添加一些额外的逻辑......

    【讨论】:

    • 对我来说就像一个魅力。在作业失败后能够通知/记录/执行任何操作非常有用。谢谢!
    【解决方案2】:

    只需在您的工作方法中添加PerformContext;您还可以从此对象访问您的JobId。对于尝试次数,这仍然依赖于魔术字符串,但它比当前/唯一的答案少一点:

    public void SendEmail(PerformContext context, string emailAddress)
    {
        string jobId = context.BackgroundJob.Id;
        int retryCount = context.GetJobParameter<int>("RetryCount");
        // send an email
    }
    

    【讨论】:

    • 你如何将PerformContext 传递给方法?
    • 根据@Olson.dev 链接:... just pass it a null in your job definition, and it is automatically substituted with a correct instance later.
    【解决方案3】:

    您可以使用IServerFilterOnPerformingOnPerformed 方法,如果您想检查尝试,或者您可以等待OnStateElectionIElectStateFilter。我不知道你有什么要求,所以这取决于你。这是你想要的代码:)

    public class JobStateFilter : JobFilterAttribute, IElectStateFilter, IServerFilter
    {
        public void OnStateElection(ElectStateContext context)
        {
            // all failed job after retry attempts comes here
            var failedState = context.CandidateState as FailedState;
    
            if (failedState == null) return;
        }
    
        public void OnPerforming(PerformingContext filterContext)
        {
            // do nothing
        }
    
        public void OnPerformed(PerformedContext filterContext)
        {
            // you have an option to move all code here on OnPerforming if you want.
            var api = JobStorage.Current.GetMonitoringApi();
    
            var job = api.JobDetails(filterContext.BackgroundJob.Id);
    
            foreach(var history in job.History)
            {
                // check reason property and you will find a string with
                // Retry attempt 3 of 3: The method or operation is not implemented.            
            }
        }   
    }
    

    如何添加过滤器

    GlobalJobFilters.Filters.Add(new JobStateFilter());
    
    ----- or 
    
    var options = new BackgroundJobServerOptions
    {   
        FilterProvider = new JobFilterCollection { new JobStateFilter() };
    };
    
    app.UseHangfireServer(options, storage);
    

    样本输出:

    【讨论】:

    • 依赖文本充其量是骇人听闻的......,随时可能崩溃......但这总比没有好......谢谢!。
    • 使用OnStateApplied 而不是OnStateElection 来避免重试问题。见stackoverflow.com/a/50253879/2279059
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-05-16
    • 1970-01-01
    • 2021-12-18
    • 2022-05-20
    相关资源
    最近更新 更多