【问题标题】:Hangfire Schedule Background tasks for different serverHangfire 为不同服务器安排后台任务
【发布时间】:2019-10-30 20:58:38
【问题描述】:

我有一个使用多个 Hangfire 服务器的 .Net 应用程序。

我希望能够让一个 Hangfire RecurringJob 触发多个 BackgroundJobs,它们可以被任何可用的服务器拾取。目前,每当我从 Hangfire 作业安排后台作业时,只有安排它们的服务器会处理它们。

例如,我有 5 个 Hangfire 服务器和 10 个任务。 我希望每个 Hangfire 服务器上有 2 个任务,而不是我看到 1 个服务器有 10 个任务,4 个有 0 个任务。

所以我有 5 个 Hangfire 服务器,都使用同一个数据库,还有 1 个 RecurringJob,这个 RecurringJob 只是读取一些文件并将几个后台作业排入队列。

 foreach (var file in reportSourceSetFileList)
 {
      _logger.LogInformation($"Queuing Background job for: {file}");

      var backgroundJobId = BackgroundJob.Enqueue<IJobHandler>(job => job.ProcessFile(file, files, null));
 }

但是,只有运行 RecurringJob 的 Hangfire Server 才能处理 Enqueued 作业。

我怎样才能让我的 5 个 Hangfire 服务器中的任何一个处理这些排队的作业,而不仅仅是将它们排队的那个?

【问题讨论】:

  • 您好,请教这个问题:您找到解决方案了吗?我有一个 Hangfire 服务器在同一台机器上的不同应用程序上运行,并希望将作业排入队列,但 Hangfire 客户端抱怨 JobStorage 未初始化,即使两者都指向同一个数据库:/
  • @s.m.我发布了这个问题的答案,说明我是如何解决这个问题的。
  • 我不确定这是否能解决您的问题,因为它仍在使用 JobStorage api 来获取 hangfire 服务器的状态。
  • 您知道为什么只有创建作业的服务器在处理作业吗?通常,据我了解,如果您在默认队列中创建作业并且所有 hangfire 服务器都在处理默认队列,那么任何 hangfire 服务器都应该选择任何作业。
  • 我不能说为什么作业一次只能在一台服务器上完成。我实际上给 Hangfire 的一位主要开发人员发了消息,他说如果有多个服务器查看同一个队列,它们将作为溢出服务器工作。这意味着只有在另一台服务器已经在使用其所有工作人员时,他们才会开始接手工作。这不是我希望它们工作的方式,但它被设计为以这种方式工作。

标签: c# asp.net-core hangfire


【解决方案1】:

Hangfire 中没有内置功能可以在多个 Hangfire 服务器之间使用循环类型的负载平衡器。

我的解决方案是使用排队系统。当每个 Hangfire 服务器启动时,它们都会获得一个任务标识符,即 GUID,我还向该服务器添加了一个唯一队列,该队列使用与其名称相同的 GUID。

所以每个服务器将查看 2 个队列,默认和 GUID。

然后我使用以下代码来查找当前处理的作业最少的服务器。

private string GetNextAvailableServer()
{
    var serverJobCounts = new Dictionary<string, int>();

    //get active servers
    var serverList = JobStorage.Current.GetMonitoringApi().Servers();
    foreach (var server in serverList)
    {
        if (server.Heartbeat.Value > DateTime.Now.AddMinutes(-1))
        {
            serverJobCounts.Add(server.Name, 0);
            foreach (var queue in server.Queues)
            {
                var currentQueues = JobStorage.Current.GetMonitoringApi().Queues();
                serverJobCounts[server.Name] += (int?)currentQueues.FirstOrDefault(e => e.Name == queue)?.Length ?? 0;
            }
        }
    }

    var jobs = JobStorage.Current.GetMonitoringApi().ProcessingJobs(0, int.MaxValue);
    foreach (var job in jobs)
    {
        if (serverJobCounts.ContainsKey(job.Value.ServerId))
        {
            serverJobCounts[job.Value.ServerId] += 1;
        }
    }

    var nextServer = serverJobCounts.OrderBy(e => e.Value).FirstOrDefault().Key;
    return nextServer.Split(':')[0].Replace("-", string.Empty, StringComparison.InvariantCulture);
}

这将返回作业最少的服务器的 GUID,它也是队列的名称。因此,您可以将下一个作业安排到当前处理的作业最少的特定队列中。

var nextServer = GetNextAvailableServer();

var client = new BackgroundJobClient();
var state = new EnqueuedState(nextServer);
var enqueueJob = client.Create<IJobHandler>(job => job.ProcessFile(file, files, null), state);

此外,当我编写此 Hangfire 时,不允许在队列名称中使用连字符,因此我通过字符串操作使 GUID 工作。我认为最新版本的 hangfire 允许您在名称中使用连字符。

需要注意的一点是,当你们中的一个服务器死机时,此解决方案就会中断。由于如果观察该队列的服务器在处理该作业之前死亡,则该作业被赋予一个唯一的队列,它将永远不会被拾取。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2018-07-24
    • 2021-09-25
    • 2020-01-30
    • 1970-01-01
    • 1970-01-01
    • 2014-01-20
    • 1970-01-01
    • 2022-06-15
    相关资源
    最近更新 更多