检查Hangfire定时作业是否已经在运行

5
我有一个使用Hangfire进行后台任务和作业的Web应用程序。
我有一个重复的作业,其中不能同时执行2个此类作业实例。假设该作业需要更长时间才能执行。
我想要检查作业当前是否正在运行,如果是,则不再重新运行。
我已经四处寻找信息,但没有找到任何相关内容。

2
这里的注释提供了一些指导:http://docs.hangfire.io/en/latest/background-methods/performing-recurrent-tasks.html。基本上,没有*好*的方法,但有一些潜在的解决方法。 - Chris Pratt
@ChrisPratt 谢谢你。是的,我也觉得没有明显的方法来做到这一点。 - Wesley Skeen
4个回答

14

有很多地方已经回答了部分问题,但是为了让我完全实现这个功能,我必须要做 两件事:

  1. 知道属性
  2. 把它们放在接口上而不是实现方法上

因此,如果您正在使用接口(例如使用依赖注入)启动Hangfire作业,请将它们放在接口定义上。

[DisableConcurrentExecution(10)]
[AutomaticRetry(Attempts = 0, LogEvents = false, OnAttemptsExceeded = AttemptsExceededAction.Delete)]

如果您没有以这种方式使用AutomaticRetry属性,则默认情况下作业将被添加到计划作业列表中,等待第一个作业完成时运行。

这样可以按照我想要的方式正常工作:

每5分钟运行一次作业。如果在接下来的5分钟时间间隔内,前一个作业仍在运行,则不会排队新实例。

如果您将它们放在实现接口的方法上,则无效。


3
我们正在使用作业过滤器,特别是这个: MaximumConcurrentExecutions - https://github.com/alastairtree/Hangfire.MaximumConcurrentExecutions 它可以防止多个作业实例同时启动。
你还可以查看DisableConcurrentExecution - https://discuss.hangfire.io/t/disableconcurentexecution-does-not-appear-to-work/158。 两者都有类似的作用,前者允许您将最大值限制为一个数字> 1,而使用后者将不允许任何并发处理作业。
但是,如果您在同一个函数中处理多个不同类型的作业,则需要更新过滤器以检查您想要限制的特定作业类型。

1

此答案适用于 Hangfire.Core 1.7.30.0

它具有以下接口

// Assembly Hangfire.Core, Version=1.7.30.0, Culture=neutral, PublicKeyToken=null
namespace Hangfire.Storage
{
    public interface IMonitoringApi
    {
        ...
        JobList<EnqueuedJobDto> EnqueuedJobs(string queue, int from, int perPage);
        JobList<FetchedJobDto> FetchedJobs(string queue, int from, int perPage);
        JobList<ProcessingJobDto> ProcessingJobs(int from, int count);
        JobList<ScheduledJobDto> ScheduledJobs(int from, int count);
        JobList<SucceededJobDto> SucceededJobs(int from, int count);
        JobList<FailedJobDto> FailedJobs(int from, int count);
        JobList<DeletedJobDto> DeletedJobs(int from, int count);
        ...
    }
}

使用方法如下:

private readonly IMonitoringApi _api;
public object_constructor(Hangfire.JobStorage jobStorage)
{
    _api = jobStorage.GetMonitoringApi();
}

public List<dynamic> GetProcessingJobs(int? from = null, int? count = null)
{
    var list = _api.ProcessingJobs(from ?? 0, count ?? int.MaxValue);
    if (list == null) return new List<dynamic>();
    var items = list.Where(kv => kv.Value.Job != null)
         .Select(kv => new {
            kv.Key,
            kv.Value.Job.Method.Name,
            kv.Value.InProcessingState,
            kv.Value.ServerId,
            StartedAt = kv.Value.StartedAt, // UTC
            Duration = DateTime.UtcNow - kv.Value.StartedAt,
            Args = string.Join(",", kv.Value.Job?.Args.Select(e => e ?? "null"))
        })
        .OrderByDescending(e => e.StartedAt)
        .Cast<dynamic>()
        .ToList();
    return items;
}

0

有没有人想过更简单的解决方案?比如一个 SQL 表,其中包含一个位字段来指示“正在处理”。作业将在最开始检查此字段。如果为真,则退出。完成后,将其设置为假。 你有什么想法吗?


1
我希望,此时此刻他们的口号应该是“Hangfire,让后台作业变得困难”-_- - Douglas Gaskell

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接