如何防止Hangfire定时任务在连续执行30分钟后重新启动

17
我正在开发一个asp.net mvc-5网页应用程序,使用Hangfire工具运行长时间的后台作业时遇到了问题。问题在于,如果作业执行时间超过30分钟,那么hangfire将自动启动另一个任务,这样我最终会有两个相似的作业同时运行。 现在,我有以下几点:
  1. Asp.net mvc-5
  2. IIS-8
  3. Hangfire 1.4.6
  4. Windows server 2012
我已经定义了一个定期的Hangfire作业,在每天17:00运行。 后台作业主要是扫描我们的网络以查找服务器和虚拟机,并更新数据库。当执行完成后,定期的作业将发送一封电子邮件。
之前,当执行时间少于30分钟时,定期的作业可以正常工作。但随着系统的增长,今天定期的作业的执行时间为40分钟,而不像以前的22-25分钟。我收到了2封电子邮件而不是一封(两封电子邮件的间隔大约为30分钟)。 现在我手动重新运行了作业,并注意到问题如下:
当定期的作业连续执行30分钟时,会启动定期作业的新实例,因此我将有两个实例同时运行,这就是我收到2封邮件的原因。
现在,如果定期的作业执行时间少于30分钟(例如29分钟),我就不会遇到任何问题。但是,如果定期的作业执行时间超过30分钟,则出于某种原因,hangfire将启动新的作业。尽管当我访问hangfire仪表板时,在作业执行期间,我可以发现只有一个活动作业,但是当我监视我们的数据库时,我可以从sql Profiler中看到有两个作业正在访问数据库。这种情况发生在定期的作业开始后30分钟(在我们的例子中是17:30),这就是为什么我收到了2封电子邮件,这意味着在后台运行了2个定期作业而不是一个。

请问有人能够给一些建议吗?如何避免Hangfire在当前定期作业执行超过30分钟时自动启动新的定期作业? 谢谢。

4个回答

23

你是否查看了Hangfire文档中的InvisibilityTimeout设置?

默认的SQL Server作业存储实现使用一个普通表作为作业队列。为了确保在意外进程终止的情况下作业不会丢失,只有在成功完成时才从队列中删除。

为了使它对其他工作人员不可见,使用带有输出子句的UPDATE语句来获取排队的作业并以原子方式更新FetchedAt值(表示已获取),其他工作人员可以看到获取时间戳,并忽略作业。但为了处理进程终止,他们只会在指定的一段时间内忽略一个作业(默认为30分钟)。

虽然这种机制确保每个作业都将被处理,但有时它可能会导致长时间重试延迟或导致多个作业执行。考虑以下场景:

  1. Worker A获取了一个作业(运行一个小时)并在12:00开始。
  2. Worker B在12:30获取了相同的作业,因为默认的不可见超时已经过期。
  3. Worker C(未获取)在13:00获取了相同的作业,因为它将在成功执行后被删除。

如果您正在使用取消标记,则WorkerA的取消标记将于12:30设置,而WorkerB的取消标记将于13:00设置。这可能导致您的长时间运行作业永远不会被执行。如果您没有使用取消标记,则WorkerA和WorkerB将同时执行它(自12:30以来),但WorkerC将不会获取它,因为它将在成功执行后被删除。

因此,如果您有长时间运行的作业,最好配置不可见超时间隔:

var options = new SqlServerStorageOptions
{
    InvisibilityTimeout = TimeSpan.FromMinutes(30) // default value
};

GlobalConfiguration.Configuration.UseSqlServerStorage("<name or connection string>", options);

Hangfire 1.5开始,这个选项现在已经过时。正在处理的作业对其他工作人员不可见。

使用SQL Server时,不再需要担心令人困惑的不可见超时和意外的后台作业重试(默认为30分钟)。新的Hangfire.SqlServer实现使用普通的事务来获取后台作业并将它们隐藏起来,使其对其他工作人员不可见。

即使出现非正常关闭,也可以立即为其他工作者提供作业,不会有任何延迟。


1
如果你预计运行时间不超过一小时,那么使用60就可以了。否则,请查看Hangfire内的MSMQ扩展 - jamesSampica
1
@Shoe:InvisibilityTimeout 属性已被标记为过时。有没有替代方案?如何处理超时? - Yogi
3
@Yogi,我已更新到Hangfire 1.5版本。这个选项不再必要,因为正在处理的作业现在本质上对其他工作者是不可见的。 - jamesSampica
1
@vast 请看一下我上一个段落,关于这已经过时的说明。 - jamesSampica
我仍然有关于 OracleStorageOptions 的问题:我没有使用 InvisibilityTimeout,但后台任务在30分钟后重试了!! 我使用HF 1.7.28。 - Alexander
显示剩余2条评论

18

我在寻找如何在Postgresql数据库中正确执行此操作的文档时遇到了困难,因为我看到的所有示例都是使用sqlserver。我在这里找到了PostgreSqlStorageOptions对象内部的invisibility timeout属性:https://github.com/frankhommers/Hangfire.PostgreSql/blob/master/src/Hangfire.PostgreSql/PostgreSqlStorageOptions.cs#L36。通过试错,我幸运地发现UsePostgreSqlStorage有一个重载来接受这个对象。在.Net Core 2.0中,当您在启动类的ConfigureServices方法中设置hangfire postgresql DB时,请添加以下内容(默认超时设置为30分钟):

    services.AddHangfire(config =>
            config.UsePostgreSqlStorage(Configuration.GetConnectionString("Hangfire1ConnectionString"), new PostgreSqlStorageOptions {
                InvisibilityTimeout = TimeSpan.FromMinutes(720)

            }));

6

我在使用 Hangfire.MemoryStorage 作为存储提供程序时遇到了这个问题。使用内存存储,你需要在 MemoryStorageOptions 中设置 FetchNextJobTimeout,否则默认情况下作业将在30分钟后超时并执行新的作业。

var options = new MemoryStorageOptions
{
    FetchNextJobTimeout = TimeSpan.FromDays(1)
};
GlobalConfiguration.Configuration.UseMemoryStorage(options);

5
这应该在文档中。 - Natalie Perret

6
我想指出,尽管下面的thing已经说明:

Hangfire 1.5开始,此选项现在已过时。正在处理的作业对其他工作者是不可见的。

使用SQL Server时,可以告别令人困惑的不可见超时以及默认情况下30分钟后(通过重试)出现的意外后台作业重试。新的Hangfire.SqlServer实现使用普通的事务来获取后台作业并将其隐藏起来,使其对其他工作者不可见。

即使发生非正常关闭,作业也会立即对其他工作者可用,没有任何延迟。

对于许多使用MySQL、PostgreSQL、MongoDB的人来说,InvisibilityTimeout仍然是最好的选择:https://github.com/HangfireIO/Hangfire/issues/1197

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