ASP.NET Core IHostedService手动启动/停止/暂停(?)



我想在ASPNET Core中实现一个可按需停止和启动的重复(定时(IHostedService实例。我的理解是IHostedService是在应用程序启动时由框架启动的。

然而,我希望能够"手动"启动/停止服务,也许可以通过UI使用打开/关闭切换。理想情况下,"关闭"状态将处理当前正在运行的服务,然后"打开"状态将创建一个新实例。

我在这里阅读了MS文档:https://learn.microsoft.com/en-us/aspnet/core/fundamentals/host/hosted-services?view=aspnetcore-2.1.

我最初的想法是获得一个正在运行的服务的实例,然后调用公共StopAsync(CancellationToken token)方法。然而,当谈到我应该传递哪个令牌时,我有点卡住了,StartAsync(CancellationToken cancellationToken)方法也是如此。

关于应该如何做到这一点,或者这是否明智,有什么想法吗?我的方法是否在某种程度上违背了ASPNET Core中托管服务的预期设计?

2018年7月7日版

因此,经过更多的研究(也就是阅读文档:D(,托管服务StartAsync/StopAsync方法确实意味着与应用程序的生存期相一致。注册的IHostedServices似乎没有添加到DI容器中,以便注入到其他类中。

因此,我认为我最初的想法不会奏效。目前,我使用可以在运行时更新的配置依赖项(IOptions<T>(注册了我的服务。当托管服务正在处理时,它会检查配置,看看是否应该继续,否则它只会等待(而不是停止或处理托管服务(。

我可能很快就会把这个作为我的答案,除非我听到其他想法。

对于StopAsync(CancellationToken token),可以传递new System.Threading.CancellationToken()。在public CancellationToken(bool canceled)的定义中,canceled表示令牌的状态。对于您的场景,不需要指定canceled,因为您希望停止服务。

你可以按照下面的步骤一步一步:

  1. 创建IHostedService

    public class RecureHostedService : IHostedService, IDisposable
    {
    private readonly ILogger _log;
    private Timer _timer;
    public RecureHostedService(ILogger<RecureHostedService> log)
    {
    _log = log;
    }
    public void Dispose()
    {
    _timer.Dispose();
    }
    public Task StartAsync(CancellationToken cancellationToken)
    {
    _log.LogInformation("RecureHostedService is Starting");
    _timer = new Timer(DoWork,null,TimeSpan.Zero, TimeSpan.FromSeconds(5));
    return Task.CompletedTask;
    }
    public Task StopAsync(CancellationToken cancellationToken)
    {
    _log.LogInformation("RecureHostedService is Stopping");
    _timer?.Change(Timeout.Infinite, 0);
    return Task.CompletedTask;
    }
    private void DoWork(object state)
    {
    _log.LogInformation("Timed Background Service is working.");
    }
    }
    
  2. 寄存器IHostedService

    services.AddSingleton<IHostedService, RecureHostedService>();
    
  3. 启动和停止服务

    public class HomeController : Controller {
    private readonly RecureHostedService _recureHostedService;
    public HomeController(IHostedService hostedService)
    {
    _recureHostedService = hostedService as RecureHostedService;
    }
    public IActionResult About()
    {
    ViewData["Message"] = "Your application description page.";
    _recureHostedService.StopAsync(new System.Threading.CancellationToken());
    return View();
    }
    public IActionResult Contact()
    {
    ViewData["Message"] = "Your contact page.";
    _recureHostedService.StartAsync(new System.Threading.CancellationToken());
    return View();
    } }
    

使用Blazor Server,可以通过以下方式启动和停止后台服务。Asp.net核心MVC或Razor是相同的原理

首先,实现IHostService

public class BackService : IHostedService, IDisposable
{
private readonly ILogger _log;
private Timer _timer;
public bool isRunning { get; set; }
public BackService(ILogger<V2rayFlowBackService> log)
{
_log = log;
}
public void Dispose()
{
_timer.Dispose();
}
public Task StartAsync(CancellationToken cancellationToken)
{
_log.LogInformation($"begin {DateTime.Now}");
_timer = new Timer(DoWorkAsync, null, TimeSpan.Zero, TimeSpan.FromSeconds(10));
return Task.CompletedTask;
}
public Task StopAsync(CancellationToken cancellationToken)
{
isRunning = false;
_log.LogInformation($"{DateTime.Now} BackService is Stopping");
_timer?.Change(Timeout.Infinite, 0);
return Task.CompletedTask;
}
private void DoWorkAsync(object state)
{
_log.LogInformation($"Timed Background Service is working.  {DateTime.Now}");
try
{
isRunning = true;
// dosometing you want
}
catch (Exception ex)
{
isRunning = false;
_log.LogInformation("Error {0}", ex.Message);
throw ex;
}
}
}

Startup.cs 中的注册服务

public void ConfigureServices(IServiceCollection services)
{
services.AddSingleton<BackService>();
services.AddHostedService(sp => sp.GetRequiredService<BackService>());
}

将后台服务注入Blazor组件

public class IndexBase:ComponentBase
{
[Inject]
BackService BackService { set; get; }
protected override void OnInitialized()
{
if (BackService.isRunning)
{
BackService.StopAsync(new System.Threading.CancellationToken());
}
base.OnInitialized();
}
public void on()
{
if (!BackService.isRunning)
{
BackService.StartAsync(new System.Threading.CancellationToken());
}
}
public void off()
{
if (BackService.isRunning)
{
BackService.StopAsync(new System.Threading.CancellationToken());
}
}
}
@page "/"
@inherits IndexBase
<h1>Hello, world!</h1>
Welcome to your new app.
<button @onclick="on">Start</button>
<button @onclick="off">Stop</button>

参考

您应该从自己的Interface继承HostedService,并将您的服务注册为Singleton,但方式不同:

首先使用AddHostedService通用方法注册服务。

services.AddHostedService<TimerHostedService>();

然后将一个公共静态字段添加到名为Instance的类中,该字段保存类的实例引用,并在构造函数中设置其值

然后在ConfigureServices中放置一个工厂,用于将服务注册为返回静态实例字段的singleton!

以下是示例代码:(在HostedService.cs:中(

public interface ITimerHostedService : IHostedService
{
}
public class TimerHostedService : ITimerHostedService
{
private static TimerHostedService _instance;
public static TimerHostedService Instance => _instance;
public TimerHostedService(ILogger<TimerHostedService> logger)
{
if(_instance == null)
{
_instance = this;
}
}
}

下面是将服务注册为singleton的代码(在Startup.cs中(:

services.AddHostedService<TimerHostedService>();
services.AddSingleton<ITimerHostedService, TimerHostedService>(serviceProvider =>
{
return TimerHostedService.Instance;
});

这是控制器中手动启动/停止HostedService:的代码

public class HomeController : Controller
{
private readonly ILogger<HomeController> _logger;
private readonly ITimerHostedService _hostedService;
public HomeController(ILogger<HomeController> logger, ITimerHostedService hostedService)
{
_logger = logger;
_hostedService = hostedService;
}
public async Task<IActionResult> Start()
{
await _hostedService.StartAsync(default);

return Ok();
}
public async Task<IActionResult> Stop()
{
await _hostedService.StopAsync(default);

return Ok();
}
}

快乐编码!

享受你的美好时刻:X

最新更新