【问题标题】:Multiple Workers in .net core worker services?.net 核心工作人员服务中有多个工作人员?
【发布时间】:2022-01-02 16:55:16
【问题描述】:

dot net core 3.0 worker services模板如下:

public class Program
{
    public static void Main(string[] args)
    {
        CreateHostBuilder(args).Build().Run();
    }
    public static IHostBuilder CreateHostBuilder(string[] args) =>
        Host.CreateDefaultBuilder(args)
            .ConfigureServices(services =>
            {
                services.AddHostedService<Worker>();
            });
}

“Worker”类派生自BackgroundService。它每 1000 毫秒循环将日志写入控制台。

我的问题:

我可以同时运行多个“Worker”吗? (我知道我可以创建另一个类“Worker2”。但是我可以运行同一个类“Worker”的两个副本吗?)

如果是,我如何配置两个具有不同配置或参数的“Worker”,例如,两个具有不同循环间隔的 Worker? (因为“Worker”类的实例是由DI框架创建的。我不知道如何将不同的配置/参数传递给“Worker”的两个不同实例)

【问题讨论】:

  • 很好奇你为什么想要或需要这样做?

标签: .net-core


【解决方案1】:

你可以有一个“父”工人像这样启动“真正的”工人......

        protected override async Task ExecuteAsync(CancellationToken stoppingToken)
        {
            while (!stoppingToken.IsCancellationRequested)
            {
                var workers = new List<Task>();
                foreach(var delay in _config.LoopIntervals)
                    workers.Add(DoRealWork(delay, stoppingToken));

                await Task.WhenAll(workers.ToArray());
            }
        }

那么……

        private async Task DoRealWork(int delay, CancellationToken stoppingToken)
        {
            while (!stoppingToken.IsCancellationRequested)
            {
                _logger.LogInformation("worker {delay} checking in at {time}", delay, DateTimeOffset.Now);
                await Task.Delay(delay, stoppingToken);
            }
        }

_config 从 appSettings.json 填充并像这样传递给 Worker 的构造函数...

var cfg = hostContext.Configuration.GetSection(nameof(WorkerConfig)).Get<WorkerConfig>();
services.AddSingleton(cfg);
services.AddHostedService<Worker>();

还有 appSettings...

{
  "WorkerConfig": {
    "LoopIntervals": [ 1000, 2000, 3000 ]
  }
}

【讨论】:

    【解决方案2】:

    “AddHostedService”不接受同一类注册两次的原因:

    查看了“AddHostedService”的github源码,发现实现如下:

    services.TryAddEnumerable(ServiceDescriptor.Singleton<IHostedService, THostedService>());
    

    根据“TryAddEnumerable”方法的 Microsoft 文档,仅当集合不包含相同服务和实现类型的其他注册时才会添加服务。这就是为什么我不能运行两个“Worker”副本的原因。 (故意使用“TryAddEnumerable”来避免为同一服务创建多个实例。参考:https://github.com/aspnet/Extensions/issues/1078


    解决方案:

    所以我可以通过直接使用“AddSingleton”来添加两个工人......

    services.AddSingleton<IHostedService, Worker>();
    services.AddSingleton<IHostedService, Worker>();
    

    有效。


    向服务构造函数传递参数:

    然后,我修改了 Worker 类的构造函数,为循环间隔添加了第二个参数。最后,我使用实现工厂注册服务如下

    services.AddSingleton<IHostedService>(sp => new Worker(sp.GetService<ILogger<Worker>>(), 1000));
    services.AddSingleton<IHostedService>(sp => new Worker(sp.GetService<ILogger<Worker>>(), 2000));
    

    【讨论】:

    • 这真是太好了,我有同样的情况,这解决了我的问题
    • 您是否遇到过使用此方法的 CancellationTokens 问题?似乎只有第一个会正常关闭。
    • @Roger 我没有测试取消令牌。我认为 Ed MacDonald 的解决方案更优雅,应该很好地处理取消令牌。
    【解决方案3】:

    您好,我也在寻找运行多个后台服务实例的解决方案

    感谢Raymond Wong,解决方案完美运行。

    只是想进一步扩展它以使其使用 AppSettings 进行配置

    "AppSettings":{
       "MaxInstances":2
    }
    

    然后在注册后台服务的时候

    var limit = appSettings.MaxInstances;
    for (int i = 0; i < limit ; i++)
    {
       services.AddSingleton<IHostedService, Worker>();
    }
    

    所以我们可以从 azure 配置中控制实例的数量。

    【讨论】:

      最近更新 更多