简体   繁体   English

多个 AddHostedService dotnet core

[英]Multiple AddHostedService dotnet core

When i tried to register more that one AddHostedService the method StartAsync is invoke only on the first当我尝试注册多个AddHostedService时,仅在第一个调用StartAsync方法

services.AddHostedService<HostServiceBox>(); // StartAsync is called
services.AddHostedService<HostServiceWebSocket>(); // DO NOT WORK StartAsync not called
services.AddHostedService<HostServiceLogging>(); // DO NOT WORK StartAsync not called

any idea?任何想法?

Thanks for the helps感谢您的帮助

Below a code who work I get around the problem by creating a helper在工作的代码下方,我通过创建帮助程序来解决问题

@statup.cs @statup.cs

public void ConfigureServices(IServiceCollection services)
        {
            JwtBearerConfiguration(services);

            services.AddCors(options => options.AddPolicy("CorsPolicy", builder =>
            {
                builder
                    .AllowAnyMethod()
                    .AllowAnyHeader()
                    .AllowAnyOrigin()
                    .AllowCredentials();
            }));

            services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1); ;


            services.AddSignalR();

            services.AddHostedService<HostServiceHelper>(); // <===== StartAsync is called

        }

@HostServiceHelper.cs @HostServiceHelper.cs

    public class HostServiceHelper : IHostedService
    {
        private static IHubContext<EngineHub> _hubContext;

        public HostServiceHelper(IHubContext<EngineHub> hubContext)
        {
            _hubContext = hubContext;
        }

        public Task StartAsync(CancellationToken cancellationToken)
        {
            return Task.Run(() =>
            {
                Task.Run(() => ServiceWebSocket(), cancellationToken);

                Task.Run(() => ServiceBox(), cancellationToken);

                Task.Run(() => ServiceLogging(), cancellationToken);

            }, cancellationToken);
        }

        public void ServiceLogging()
        {
        // your own CODE
         }

        public void ServiceWebSocket()
        {
 // your own CODE
        }

        public void ServiceBox()
        {
            // your own CODE
        }

        public Task StopAsync(CancellationToken cancellationToken)
        {
            //Your logical
            throw new NotImplementedException();
        }
    }

A hosted service is usually a single task so I'd do it with a singleton.托管服务通常是一个单一的任务,所以我会用一个单例来完成。

// Hosted Services
services.AddSingleton<IHostedService, HttpGetCurrencyPairRequestSyncingService>();
services.AddSingleton<IHostedService, HttpPostCurrencyPairRequestSyncingService>();

And when in my class,在我上课的时候,

public class CurrencyPairCacheManagementService : BaseHostedService<CurrencyPairCacheManagementService>
        , ICurrencyPairCacheManagementService, IHostedService, IDisposable
    {
        private ICurrencyPairService _currencyPairService;
        private IConnectionMultiplexer _connectionMultiplexer;

        public CurrencyPairCacheManagementService(IConnectionMultiplexer connectionMultiplexer,
            IServiceProvider serviceProvider) : base(serviceProvider)
        {
            _currencyPairService = serviceProvider.GetService<CurrencyPairService>();
            _connectionMultiplexer = connectionMultiplexer;

            InitializeCache(serviceProvider);
        }

        /// <summary>
        /// Operation Procedure for CurrencyPair Cache Management.
        ///
        /// Updates every 5 seconds.
        ///
        /// Objectives:
        /// 1. Pull the latest currency pair dataset from cold storage (DB)
        /// 2. Cross reference checking (MemoryCache vs Cold Storage)
        /// 3. Update Currency pairs
        /// </summary>
        /// <param name="stoppingToken"></param>
        /// <returns></returns>
        /// <exception cref="NotImplementedException"></exception>
        protected override async Task ExecuteAsync(CancellationToken stoppingToken)
        {
            _logger.LogInformation("CurrencyPairCacheManagementService is starting.");

            stoppingToken.Register(() => _logger.LogInformation("CurrencyPairCacheManagementService is stopping."));

            while (!stoppingToken.IsCancellationRequested)
            {
                var currencyPairs = _currencyPairService.GetAllActive();

                await Task.Delay(TimeSpan.FromSeconds(5), stoppingToken);
            }

            _logger.LogWarning("CurrencyPairCacheManagementService background task is stopping.");
        }

        public void InitializeCache(IServiceProvider serviceProvider)
        {
            var currencyPairs = _currencyPairService.GetAllActive();

            // Load them individually to the cache.
            // This way, we won't have to update the entire collection if we were to remove, update or add one.
            foreach (var cPair in currencyPairs)
            {
                // Naming convention => PREFIX + CURRENCYPAIRID
                // Set the object into the cache

            }
        }

        public Task InproPair(CurrencyPair currencyPair)
        {
            throw new NotImplementedException();
        }
    }

ExecuteAsync gets hit first, before carrying on with what you want it to do. ExecuteAsync 在继续执行您希望它执行的操作之前首先被命中。 You might also want to remove the generics declaration I have because my Base class runs with generics (If you don't run your hosted service base class with generics then I don't think you'll need to inherit IHostedService and IDisposable explicitly).您可能还想删除我的泛型声明,因为我的基类使用泛型运行(如果您不使用泛型运行托管服务基类,那么我认为您不需要显式继承 IHostedService 和 IDisposable)。

OK, this is 2022 and .NET 6 is out.好的,这是 2022 年,.NET 6 已经出局。 Now days it is not a problem to run multiple hosted services, as long as they are represented by different classes.现在运行多个托管服务不是问题,只要它们由不同的类表示。 Just like this:像这样:

public class Program
{
    public static void Main(string[] args)
    {
        IHost host = Host.CreateDefaultBuilder(args)
            .ConfigureServices((hostContext, services) =>
            {
                services.AddHostedService<Worker>();
                services.AddHostedService<Worker2>();
            }).Build();

        host.Run();
    }
}

Both workers will run.两个工人都会跑。

But what if we need multiple instances of the same service class to run parallel?但是,如果我们需要同一服务 class 的多个实例来并行运行呢? That seems to be still impossible.这似乎仍然是不可能的。

I ended up implementing my own utility function to start multiple tasks in parallel and collect all the exceptions properly.我最终实现了自己的实用程序 function 以并行启动多个任务并正确收集所有异常。 Here:这里:

/// <summary>
/// Runs multiple cancelable tasks in parallel. If any of the tasks terminates, all others are cancelled. 
/// </summary>
public static class TaskBunchRunner
{
    public class BunchException : Exception
    {
        public AggregateException Agg { get; }

        public BunchException(AggregateException agg) : base("Task bunch failed", agg)
        {
            Agg = agg;
        }

        public override string Message => $"Task bunch failed: {Agg.Message}";
        public override string ToString() => $"BunchException -> {Agg.ToString()}";
    }
    
    public static async Task Bunch(this IEnumerable<Func<CancellationToken, Task>> taskFns, CancellationToken ct)
    {
        CancellationTokenSource combinedTcs = CancellationTokenSource.CreateLinkedTokenSource(ct);
        CancellationToken ct1 = combinedTcs.Token;

        Task[] tasks = taskFns.Select(taskFn => Task.Run(() => taskFn(ct1), ct1)).ToArray();
        
        // If any of the tasks terminated, it may be because of an error or a cancellation.
        // In both cases we cancel all of them. 
        await Task.WhenAny(tasks); // this await will never throw
        combinedTcs.Cancel();
        
        var allTask = Task.WhenAll(tasks); // this will collect exceptions in an AggregateException
        try
        {
            await allTask;
        }
        catch (Exception)
        {
            if (allTask.Exception != null) throw new BunchException(allTask.Exception);
            throw; 
        }
        
        // Why not just await Task.WhenAll() and let it throw whatever it is?
        // Because await will unwrap the aggregated exception and rethrow just one of the inner exceptions,
        // losing the information about others. We want all the exceptions to be logged, that is why 
        // we get the aggregated exception from the task. We also throw it wrapped into a custom exception, so the 
        // outer await (in the caller's scope) does not unwrap it again. :facepalm:
    }
}

Now we create a single hosted service and make its ExecuteAsync method run several tasks as a bunch:现在我们创建一个托管服务并使其ExecuteAsync方法作为一组运行多个任务:

class MySingleService
{
    private readonly string _id;
    public MySingleService(string id){ _id = id;  }
    
    public async Task RunAsync(CancellationToken ct)
    {
        await Task.Delay(500, ct);
        Console.WriteLine($"Message from service {_id}");
        await Task.Delay(500, ct);
    }
}

class MyHostedService: BackgroundService
{
    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        MySingleService[] individuals = new[]
        {
            new MySingleService("1"),
            new MySingleService("2"),
            new MySingleService("3"),
        };
        
        await individuals
            .Select<MySingleService, Func<CancellationToken, Task>>(s => s.RunAsync)
            .Bunch(stoppingToken);
    }
}

public class Program
{
    public static void Main(string[] args)
    {
        IHost host = Host.CreateDefaultBuilder(args)
            .ConfigureServices((hostContext, services) =>
            {
                services.AddHostedService<MyHostedService>();
            }).Build();

        host.Run();
    }
}

Note: the TaskBunchRunner class was taken from a real project and proven to work, while the usage example is made up and not tested.注意: TaskBunchRunner class 取自一个真实项目并被证明可以工作,而使用示例是编造的,未经测试。

I know it is not exactly what the OP asked for.我知道这不是 OP 所要求的。 But may be useful for someone who ends up here having the same problem as I had.但是对于最终在这里遇到与我相同的问题的人可能很有用。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM