如何配置托管服务在 .net core 2.2 windows 服务完全启动后开始?
How to configure hosted service to begin after .net core 2.2 windows service fully started?
Here is sample of windows service behaviour where hosted service is trying to connect before application started
我有一个 .net core 2.2 控制台应用程序配置为 运行 作为 windows 服务。我还配置了一个托管服务,它尝试无限期地与外部 tcp 服务器建立连接,直到连接成功。
问题是当 windows 服务启动且外部 tcp 服务器不可用时,windows 服务挂起在 'starting' 状态,直到外部服务可用于连接。该服务永远不会达到 'running' 状态。虽然此行为与文档 https://docs.microsoft.com/en-us/aspnet/core/fundamentals/host/hosted-services?view=aspnetcore-2.2&tabs=visual-studio#ihostedservice-interface 一致,其中说 "When using the Generic Host, StartAsync is called before ApplicationStarted is triggered.",但我想要的是在 windows 服务完全启动后(即处于 'running' 状态)开始托管服务.
我确实研究了使托管服务成为定时后台服务的方面,其初始延迟足以完全启动 windows 服务,而不是使用 IApplicationLifetime.ApplicationStarted 注册托管服务,但感觉就像一种估计 windows 服务启动所需时间的怪异方法。相反,我正在寻找一种优雅的方式来在 windows 服务完全启动后开始托管服务。
托管服务代码
public class TcpHostedService : BackgroundService
{
private readonly IPostBridgeConnection _client;
private readonly IApplicationLifetime _lifetime;
private readonly ILogger<TcpHostedService> _logger;
public TcpHostedService(IPostBridgeConnection client,
IApplicationLifetime lifetime,
ILogger<TcpHostedService> logger)
{
_client = client ?? throw new ArgumentNullException(nameof(client));
_lifetime = lifetime ?? throw new ArgumentNullException(nameof(lifetime));
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
}
protected override Task ExecuteAsync(CancellationToken stoppingToken)
{
_logger.LogInformation("tcp connection - registering start and stop events with application lifetime");
_lifetime.ApplicationStarted.Register(() => OnStarted(stoppingToken));
_lifetime.ApplicationStopping.Register(() => OnStopping(stoppingToken));
_lifetime.ApplicationStopped.Register(() => OnStopped(stoppingToken));
_logger.LogInformation("tcp connection - registered start and stop events with application lifetime");
_logger.LogInformation("tcp connection backgroundService is starting");
return Task.CompletedTask;
}
#region lifetime events
private void OnStarted(CancellationToken t)
{
_logger.LogInformation("tcp connection onstarted event triggered");
_client.Init(t);
}
private void OnStopping(CancellationToken t)
{
_logger.LogInformation("tcp connection onstopping event triggered");
_client.Dispose(t);
}
private void OnStopped(CancellationToken t)
{
_logger.LogInformation("tcp connection onstopped event triggered");
_logger.LogInformation("tcp connection is disposed");
}
此服务在 Program.cs 中注册为
services.AddHostedService<TcpHostedService>();
按照您展示的方式,使用 IApplicationLifetime.ApplicationStarted
令牌(或 .NET Core 3.0 中的 IHostApplicationLifetime
)注册回调看起来应该可行。那里的限制是回调是同步的,所以你不应该在那里做任何事情 async
。
但是,就我个人而言,我更喜欢您最初描述的方法 - 将 IHostedService
设置为 BackgroundService
,这样它就不会阻止启动,并使用 Polly 重试任何暂时性错误,直到客户端能够连接。
Here is sample of windows service behaviour where hosted service is trying to connect before application started 我有一个 .net core 2.2 控制台应用程序配置为 运行 作为 windows 服务。我还配置了一个托管服务,它尝试无限期地与外部 tcp 服务器建立连接,直到连接成功。
问题是当 windows 服务启动且外部 tcp 服务器不可用时,windows 服务挂起在 'starting' 状态,直到外部服务可用于连接。该服务永远不会达到 'running' 状态。虽然此行为与文档 https://docs.microsoft.com/en-us/aspnet/core/fundamentals/host/hosted-services?view=aspnetcore-2.2&tabs=visual-studio#ihostedservice-interface 一致,其中说 "When using the Generic Host, StartAsync is called before ApplicationStarted is triggered.",但我想要的是在 windows 服务完全启动后(即处于 'running' 状态)开始托管服务.
我确实研究了使托管服务成为定时后台服务的方面,其初始延迟足以完全启动 windows 服务,而不是使用 IApplicationLifetime.ApplicationStarted 注册托管服务,但感觉就像一种估计 windows 服务启动所需时间的怪异方法。相反,我正在寻找一种优雅的方式来在 windows 服务完全启动后开始托管服务。
托管服务代码
public class TcpHostedService : BackgroundService
{
private readonly IPostBridgeConnection _client;
private readonly IApplicationLifetime _lifetime;
private readonly ILogger<TcpHostedService> _logger;
public TcpHostedService(IPostBridgeConnection client,
IApplicationLifetime lifetime,
ILogger<TcpHostedService> logger)
{
_client = client ?? throw new ArgumentNullException(nameof(client));
_lifetime = lifetime ?? throw new ArgumentNullException(nameof(lifetime));
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
}
protected override Task ExecuteAsync(CancellationToken stoppingToken)
{
_logger.LogInformation("tcp connection - registering start and stop events with application lifetime");
_lifetime.ApplicationStarted.Register(() => OnStarted(stoppingToken));
_lifetime.ApplicationStopping.Register(() => OnStopping(stoppingToken));
_lifetime.ApplicationStopped.Register(() => OnStopped(stoppingToken));
_logger.LogInformation("tcp connection - registered start and stop events with application lifetime");
_logger.LogInformation("tcp connection backgroundService is starting");
return Task.CompletedTask;
}
#region lifetime events
private void OnStarted(CancellationToken t)
{
_logger.LogInformation("tcp connection onstarted event triggered");
_client.Init(t);
}
private void OnStopping(CancellationToken t)
{
_logger.LogInformation("tcp connection onstopping event triggered");
_client.Dispose(t);
}
private void OnStopped(CancellationToken t)
{
_logger.LogInformation("tcp connection onstopped event triggered");
_logger.LogInformation("tcp connection is disposed");
}
此服务在 Program.cs 中注册为
services.AddHostedService<TcpHostedService>();
按照您展示的方式,使用 IApplicationLifetime.ApplicationStarted
令牌(或 .NET Core 3.0 中的 IHostApplicationLifetime
)注册回调看起来应该可行。那里的限制是回调是同步的,所以你不应该在那里做任何事情 async
。
但是,就我个人而言,我更喜欢您最初描述的方法 - 将 IHostedService
设置为 BackgroundService
,这样它就不会阻止启动,并使用 Polly 重试任何暂时性错误,直到客户端能够连接。