首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >Hangfire为不同服务器调度后台任务

Hangfire为不同服务器调度后台任务
EN

Stack Overflow用户
提问于 2019-10-31 04:58:39
回答 1查看 1.2K关注 0票数 2

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

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

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

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

代码语言:javascript
复制
 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服务器才会处理入队作业。

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

EN

回答 1

Stack Overflow用户

发布于 2020-05-06 21:37:53

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

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

因此,每台服务器将查看2个队列,默认队列和GUID。

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

代码语言:javascript
复制
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,它也是队列的名称。因此,您可以将下一个作业调度到当前处理的作业最少的特定队列中。

代码语言:javascript
复制
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允许在名称中使用连字符。

需要注意的一件事是,当你的一台服务器死机时,这个解决方案就会中断。由于一个作业被赋予了一个唯一的队列,如果在处理该作业之前监视该队列的服务器死掉了,那么它将永远不会被接收。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/58633556

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档