杭火为不同服务器安排后台任务

问题描述 投票:1回答:1

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

我希望能够让一个Hangfire RecurringJob触发多个BackgroundJob,这些BackgroundJob可以被任何可用的服务器接收。目前,每当我从一个Hangfire Job中安排BackgroundJob时,只有安排它们的服务器会处理它们。

例如,我有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服务器会处理Enqueued作业。

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

c# asp.net-core hangfire
1个回答
0
投票

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

我的解决方案是使用排队系统。当每个Hangfire服务器启动时,他们被赋予一个任务标识符,这是一个GUID,我还添加了一个独特的队列到该服务器,它使用相同的GUID作为它的名字。

所以每台服务器都会查看2个队列,Default和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不允许在队列名称中使用连字符,因此我对字符串进行了操作,使GUIDs工作。我认为最新版本的Hangfire允许你在名字中使用连字符。

有一点要注意,当你们其中一个服务器死亡时,这个解决方案就会失效。由于一个作业被赋予了一个唯一的队列,如果看管该队列的服务器在处理作业前死亡,它将永远不会被接收。

© www.soinside.com 2019 - 2024. All rights reserved.