14

我正在开发一个 asp.net mvc-5 Web 应用程序,并且在使用 Hangfire 工具运行长时间运行的后台作业时遇到了问题。问题是如果作业执行时间超过 30 分钟,hangfire 会自动启动另一个作业,所以我最终会同时运行两个类似的作业。

现在我有以下内容: -

  1. asp.net mvc-5
  2. IIS-8
  3. 挂火 1.4.6
  4. 视窗服务器 2012

现在我已经定义了一个hangfire 重复作业,在每天17:00 运行。后台作业主要扫描我们的网络中的服务器和虚拟机并更新数据库,重复作业将在完成执行后发送电子邮件。当执行时间少于 30 分钟时,重复性作业过去运行良好。但是今天随着我们系统的发展,重复性工作在 40 分钟后完成,而不是像以前那样需要 22-25 分钟。我收到了 2 封电子邮件而不是一封电子邮件(电子邮件之间的时间约为 30 分钟)。现在我手动重新运行作业,我注意到问题如下: -

“当循环作业连续执行 30 分钟时,将启动循环作业的新实例,所以我将有两个实例而不是一个同时运行,所以我收到了 2 封电子邮件。”

现在,如果重复作业的时间少于 30 分钟(例如 29 分钟),我不会遇到任何问题,但如果重复作业的执行时间超过 30 分钟,那么出于某种原因或另一个挂火将启动一个新作业。虽然当我在作业执行期间访问hangfire仪表板时,我发现只有一个活动作业,但当我监控我们的数据库时,我可以从sql分析器中看到有两个作业正在访问数据库。这发生在重复作业开始 30 分钟后(在我们的例子中是 17:30),这就是为什么我收到 2 封电子邮件,这意味着 2 个重复作业在后台运行,而不是一个。

那么,任何人都可以就此提出建议,如果当前的重复作业执行时间超过 30 分钟,我如何避免挂火自动启动新的重复作业?谢谢

4

4 回答 4

22

您是否查看InvisibilityTimeoutHangfire 文档中的设置?

默认 SQL Server 作业存储实现使用常规表作为作业队列。为确保在意外进程终止的情况下不会丢失作业,仅在成功完成后才将其从队列中删除。

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

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

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

如果您使用取消令牌,它将在 12:30 为 Worker A 设置,为 Worker B 设置为 13:00。这可能导致您的长时间运行的作业永远不会执行。如果你不使用取消令牌,它将由 WorkerA 和 Worker B 并发执行(从 12:30 开始),但 Worker C 不会获取它,因为它会在执行成功后被删除。

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

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

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

Hangfire 1.5 开始,这个选项现在是Obsolete. 其他工人看不到正在从事的工作。

在使用 SQL Server 时,告别将隐身超时与意外的后台作业重试(默认情况下)相混淆。新的 Hangfire.SqlServer 实现使用普通的旧事务来获取后台作业并将它们隐藏在其他工作人员面前。

即使在不正常的关闭之后,其他工作人员也可以立即使用该作业,没有任何延迟。

于 2015-10-30T05:38:33.613 回答
15

我很难找到有关如何为 Postgresql 数据库正确执行此操作的文档,我看到的每个示例都使用 sqlserver,我发现隐形超时是 PostgreSqlStorageOptions 对象中的一个属性,我在这里找到了这个: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)

            }));
于 2018-08-01T21:09:22.103 回答
5

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

var options = new MemoryStorageOptions
{
    FetchNextJobTimeout = TimeSpan.FromDays(1)
};
GlobalConfiguration.Configuration.UseMemoryStorage(options);
于 2019-03-26T09:45:36.393 回答
3

Just would like to point out that even though, it is stated the thing below:

As of Hangfire 1.5 this option is now Obsolete. Jobs that are being worked on are invisible to other workers.

Say goodbye to confusing invisibility timeout with unexpected background job retries after 30 minutes (by default) when using SQL Server. New Hangfire.SqlServer implementation uses plain old transactions to fetch background jobs and hide them from other workers.

Even after ungraceful shutdown, the job will be available for other workers instantly, without any delays.

It seems that for many people using MySQL, PostgreSQL, MongoDB, InvisibilityTimeout is still the way to go: https://github.com/HangfireIO/Hangfire/issues/1197

于 2019-09-05T09:59:55.633 回答