BackgroundService Graceful Shutdown - 完成工作并写入数据库
BackgroundService Graceful Shutdown - Complete work and write to DB
我有一个后台工作者实现了 BackgroundService(由 MS 提供)。
查看这个简单的实现:
public class MyService : BackgroundService {
private readonly MyDbContext _context;
public MyService(MyDbContext context) {
//...
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
try {
while (true)
{
stoppingToken.ThrowIfCancellationRequested();
// Do some work
}
} catch(OperationCancelledException) {
_context.Add(new MyLogMessage(){ Error = "MyService cancelled!" });
_context.SaveChanges();
}
// ...
}
}
当请求正常关闭(在控制台中:CTRL+C)时,catch 块被触发,而且 SaveChanges()
似乎也被执行。但是,有时错误会存储到数据库中,而大多数时候不会。此外,EntityFramework 正在控制台上打印一条插入语句,但日志不在数据库中。
我假设关闭发生得比将数据写入数据库更快?
谁能给我提示如何处理这种情况并将错误存储到数据库中?
应用程序关闭时,stoppingToken
似乎没有按预期取消。我设法使用 IHostApplicationLifetime
和一个新字段解决了这个问题,如果正在进行关机,我可以在其中存储。
public class TestService : BackgroundService {
private readonly IHostApplicationLifetime _lifetime;
private readonly ILogger<TestService> _logger;
private bool _shutownRequested;
public TestService(IHostApplicationLifetime lifetime, ILogger<TestService> logger) {
_lifetime = lifetime;
_logger = logger;
}
public override Task StartAsync(CancellationToken cancellationToken) {
_lifetime.ApplicationStopping.Register(OnShutdown);
return Task.CompletedTask;
}
private void OnShutdown() {
_shutdownRequested = true;
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken) {
try {
while(true) {
stoppingToken.ThrowIfCancellationRequested();
if(_shutdownRequested) {
throw new OperationCanceledException();
}
await Task.Delay(100, CancellationToken.None);
}
} catch(OperationCanceledException) {
_logger.LogWarning("TestService canceled");
}
}
}
现在在那里抛出一个新的异常可能会更好,但作为一个例子它就可以了。
日志条目未出现在数据库中的原因是主机关闭时间低于在 while
循环中处理任务并将日志发送到数据库所需的时间。默认超时为 5 秒。
您可以做的是将超时增加到一个更大的值,例如一分钟和两分钟:
services.Configure<HostOptions>(
opts => opts.ShutdownTimeout = TimeSpan.FromMinutes(2));
确保为服务留出足够的时间来完成 while
循环内的迭代并记录消息。
请查看Extending the shutdown timeout setting to ensure graceful IHostedService shutdown了解更多详情。
我有一个后台工作者实现了 BackgroundService(由 MS 提供)。
查看这个简单的实现:
public class MyService : BackgroundService {
private readonly MyDbContext _context;
public MyService(MyDbContext context) {
//...
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
try {
while (true)
{
stoppingToken.ThrowIfCancellationRequested();
// Do some work
}
} catch(OperationCancelledException) {
_context.Add(new MyLogMessage(){ Error = "MyService cancelled!" });
_context.SaveChanges();
}
// ...
}
}
当请求正常关闭(在控制台中:CTRL+C)时,catch 块被触发,而且 SaveChanges()
似乎也被执行。但是,有时错误会存储到数据库中,而大多数时候不会。此外,EntityFramework 正在控制台上打印一条插入语句,但日志不在数据库中。
我假设关闭发生得比将数据写入数据库更快?
谁能给我提示如何处理这种情况并将错误存储到数据库中?
应用程序关闭时,stoppingToken
似乎没有按预期取消。我设法使用 IHostApplicationLifetime
和一个新字段解决了这个问题,如果正在进行关机,我可以在其中存储。
public class TestService : BackgroundService {
private readonly IHostApplicationLifetime _lifetime;
private readonly ILogger<TestService> _logger;
private bool _shutownRequested;
public TestService(IHostApplicationLifetime lifetime, ILogger<TestService> logger) {
_lifetime = lifetime;
_logger = logger;
}
public override Task StartAsync(CancellationToken cancellationToken) {
_lifetime.ApplicationStopping.Register(OnShutdown);
return Task.CompletedTask;
}
private void OnShutdown() {
_shutdownRequested = true;
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken) {
try {
while(true) {
stoppingToken.ThrowIfCancellationRequested();
if(_shutdownRequested) {
throw new OperationCanceledException();
}
await Task.Delay(100, CancellationToken.None);
}
} catch(OperationCanceledException) {
_logger.LogWarning("TestService canceled");
}
}
}
现在在那里抛出一个新的异常可能会更好,但作为一个例子它就可以了。
日志条目未出现在数据库中的原因是主机关闭时间低于在 while
循环中处理任务并将日志发送到数据库所需的时间。默认超时为 5 秒。
您可以做的是将超时增加到一个更大的值,例如一分钟和两分钟:
services.Configure<HostOptions>(
opts => opts.ShutdownTimeout = TimeSpan.FromMinutes(2));
确保为服务留出足够的时间来完成 while
循环内的迭代并记录消息。
请查看Extending the shutdown timeout setting to ensure graceful IHostedService shutdown了解更多详情。