如何让托管服务在 asp net core 3.1 中保持活动状态?

How to keep a hosted service alive in asp net core 3.1?

你好吗?

我在 net core 3.1 中有一个网络 api,这又包含一项服务,每 X 分钟必须 运行 执行数据迁移(我只是在测试它),但我有2个问题。

  1. 为了 运行 的服务,我必须首先 运行 我的 api 中的一些 url。问题是:如何让这项服务自动启动,而不需要 运行 任何 api?
  2. 当我停止使用 apis 几分钟后,服务停止工作。问题是:如何让服务 "Forever" 保持活动状态?

我必须强调,我的网站 api 托管在虚拟主机中,在那里我无法访问 IIS 的所有功能

这是我的代码,在此先感谢您的帮助。

MySuperService.cs

 public class MySuperService : IHostedService, IDisposable
{
    private bool _stopping;
    private Task _backgroundTask;
    private static readonly log4net.ILog log =log4net.LogManager.GetLogger(typeof(MySuperService));
    public Task StartAsync(CancellationToken cancellationToken)
    {
        Console.WriteLine("MySuperService is starting.");
        log.Info("MySuperService is starting.");
        _backgroundTask = BackgroundTask();
        return Task.CompletedTask;
    }

    private async Task BackgroundTask()
    {
        int contador = 1;
        while (!_stopping)
        {
            Console.WriteLine("MySuperService is working--> " + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"));
            log.Info("MySuperService is working--> " + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"));
            await Task.Delay(TimeSpan.FromMinutes(3));
            contador++;
        }

        Console.WriteLine("MySuperService background task is stopping.");
        log.Info("MySuperService background task is stopping.");
    }

    public async Task StopAsync(CancellationToken cancellationToken)
    {
        Console.WriteLine("MySuperService is stopping.");
        log.Info("MySuperService is stopping.");
        _stopping = true;
        if (_backgroundTask != null)
        {
            // TODO: cancellation
            await BackgroundTask();
            //await _backgroundTask;
        }
    }

    public void Dispose()
    {
        Console.WriteLine("MySuperService is disposing.");
        log.Info("MySuperService is disposing.");
    }
}

Program.cs

public class Program
{
    private static readonly log4net.ILog log = log4net.LogManager.GetLogger(typeof(Program));
    public static void Main(string[] args)
    {
        ...
        CreateHostBuilder(args).Build().Run();
    }

    public static IHostBuilder CreateHostBuilder(string[] args) =>
        Host.CreateDefaultBuilder(args)
            .ConfigureWebHostDefaults(webBuilder =>
            {
                webBuilder.UseStartup<Startup>();
            }).ConfigureServices((hostContext, services) =>
            {
                services.AddHostedService<MySuperService>();
            });
}

继承自 BackgroundService 而不是实现 IHostedService。这将负责为您启动、运行 和停止服务的机制。

但是您面临的问题是 IIS 直到服务的第一个请求才启动您的 c# 进程。如果没有请求,默认应用程序池设置将再次关闭它。我建议设置某种计划任务以定期请求 url 并监视服务是否为 运行。如果它无论如何都停止了,您会希望得到通知吗?

https://docs.microsoft.com/en-us/aspnet/core/fundamentals/host/hosted-services?view=aspnetcore-3.0&tabs=visual-studio

如果您从 BackgroundService class 继承您的无限作业服务并在循环和所需的

中实现您的逻辑
await Task.Delay(TimeSpan.FromMinutes(x miutes))

一旦应用程序在没有任何 API 调用的情况下启动,作业将 运行 立即停止,并在应用程序停止时停止。

 public class MyService : BackgroundService
{
    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        while (!stoppingToken.IsCancellationRequested)
        {
            Console.WriteLine("test");

            //await run job

            await Task.Delay(TimeSpan.FromSeconds(1));
        }
    }

    public override async Task StartAsync(CancellationToken cancellationToken)
    {
        Console.WriteLine("start");

        await ExecuteAsync(cancellationToken);
    }

    public override Task StopAsync(CancellationToken cancellationToken)
    {
        Console.WriteLine("stop");

        return Task.CompletedTask;
    }
}

我想出的方法如下:

public class PingPongHostedService : IHostedService
{

    public Task StartAsync(CancellationToken cancellationToken)
    {
        Console.WriteLine(">>>>> Hosted service starting at {0}", DateTimeOffset.Now.ToUnixTimeMilliseconds());

        int count = 0;

        try
        {
            Task.Run(async () => {               // run in background and return completed task

                while (!cancellationToken.IsCancellationRequested)
                {
                    await Task.Delay(1_766, cancellationToken);
                    Console.WriteLine("loop no. {0}", ++count);
                }

            }, cancellationToken);
            
        }
        catch (OperationCanceledException e){} // Prevent throwing if the Delay is cancelled
        
        return Task.CompletedTask; // return ASAP
    }

    public Task StopAsync(CancellationToken cancellationToken)
    {
        Console.WriteLine(">>>>> Hosted service stopped at {0}", DateTimeOffset.Now.ToUnixTimeMilliseconds());
        return Task.CompletedTask;
    }
}

这里重要的是:StartAsync 必须尽快退出,这样 IGenericHost bootstrap 才能继续。这就是为什么我使用 Task.run 将实际工作转移到另一个线程,允许调用线程继续。

我很清楚这是一个相当古老的问题,并且已经有一些答案,但我想就此事提供我的意见。我使用了一个出色的库来安排名为 FluentScheduler 的任务。在我们开始之前,将此添加到您的 Startup.cs:

public void ConfigureServices(IServiceCollection services)
{
     services.AddHostedService<AppLifetimeEventsService>();
}

我就是这样解决上述问题的:

    public class AppLifetimeEventsService : IHostedService
    {
        private readonly ILogger _logger;
        public AppLifetimeEventsService(IServiceProvider services)
        {
            _logger = services.GetRequiredService<ILogger<AppLifetimeEventsService>>();
        }

        public Task StartAsync(CancellationToken cancellationToken)
        {
            _logger.LogInformation("The Web API has been started...");

            //FluentScheduler
            var registry = new Registry();
            //For example let's run our method every 1 hour or 10 seconds
            registry.Schedule(async () => await SomeBackgroundTask()).ToRunNow().AndEvery(1).Hours();
            //registry.Schedule(async () => await SomeBackgroundTask()).ToRunNow().AndEvery(10).Seconds();

            //FluentScheduler
            JobManager.Initialize(registry);

            return Task.CompletedTask;
        }

        public Task StopAsync(CancellationToken cancellationToken)
        {
            //Needed to remove all jobs from our Job manager when our Web API is shutting down  
            JobManager.RemoveAllJobs();

            _logger.LogInformation("The Web API is stopping now...");

            return Task.CompletedTask;
        }

        private async Task SomeBackgroundTask()
        {
             //Your long task goes here... In my case, I used a method with await here.
        }
    }

如果你想运行每Xseconds/minutes/hours/days/etc一个方法。您将必须实施 IHostedService: info and docs。然而,如果你只想执行一次该方法,你应该实现 BackgroundService.