.net core 基于 IHostedService 实现定时任务
.net core 基于 IHostedService 實(shí)現(xiàn)定時(shí)任務(wù)
Intro
從 .net core 2.0 開(kāi)始,開(kāi)始引入 IHostedService,可以通過(guò) IHostedService 來(lái)實(shí)現(xiàn)后臺(tái)任務(wù),但是只能在 WebHost 的基礎(chǔ)上使用。從 .net core 2.1 開(kāi)始微軟引入通用主機(jī)( GenericHost),使得我們可以在不使用 Web 的情況下,也可以使用 IHostedService 來(lái)實(shí)現(xiàn) 定時(shí)任務(wù)/Windows服務(wù)/后臺(tái)任務(wù),并且引入了一個(gè) BackgroundService 抽象類(lèi)來(lái)更方便的創(chuàng)建后臺(tái)任務(wù)。
IHostedService
IHostedService 后臺(tái)任務(wù)的執(zhí)行與應(yīng)用程序(就此而言,為主機(jī)或微服務(wù))的生存期相協(xié)調(diào)。當(dāng)應(yīng)用程序啟動(dòng)時(shí)注冊(cè)任務(wù),當(dāng)應(yīng)用程序關(guān)閉時(shí),有機(jī)會(huì)執(zhí)行某些正常操作或清理。
始終可以啟動(dòng)后臺(tái)線程來(lái)運(yùn)行任何任務(wù),而無(wú)需使用 IHostedService。不同之處就在于應(yīng)用的關(guān)閉時(shí)間,此時(shí)會(huì)直接終止線程,而沒(méi)有機(jī)會(huì)執(zhí)行正常的清理操作。
當(dāng)注冊(cè) IHostedService 時(shí),.NET Core 會(huì)在應(yīng)用程序啟動(dòng)和停止期間分別調(diào)用 IHostedService 類(lèi)型的 StartAsync() 和 StopAsync() 方法。具體而言,即在服務(wù)器已啟動(dòng)并已觸發(fā) IApplicationLifetime.ApplicationStarted 后調(diào)用 start。
namespace Microsoft.Extensions.Hosting
{
//
// Summary:
// Defines methods for objects that are managed by the host.
public interface IHostedService
{
//
// Summary:
// Triggered when the application host is ready to start the service.
Task StartAsync(CancellationToken cancellationToken);
//
// Summary:
// Triggered when the application host is performing a graceful shutdown.
Task StopAsync(CancellationToken cancellationToken);
}
}
可以從頭開(kāi)始創(chuàng)建自定義托管服務(wù)類(lèi)并實(shí)現(xiàn) IHostedService,因?yàn)樵谑褂?.NET Core 2.0 時(shí)需執(zhí)行這些操作。
但是,由于大多數(shù)后臺(tái)任務(wù)在取消令牌管理和其他典型操作方面都有類(lèi)似的需求,因此 .net core 2.1 有一個(gè)非常方便且可以從中進(jìn)行派生的抽象基類(lèi), BackgroundService 定義如下:
// Copyright (c) .NET Foundation. Licensed under the Apache License, Version 2.0.
/// <summary>
/// Base class for implementing a long running <see cref="IHostedService"/>.
/// </summary>
public abstract class BackgroundService : IHostedService, IDisposable
{
private Task _executingTask;
private readonly CancellationTokenSource _stoppingCts =
new CancellationTokenSource();
protected abstract Task ExecuteAsync(CancellationToken stoppingToken);
public virtual Task StartAsync(CancellationToken cancellationToken)
{
// Store the task we're executing
_executingTask = ExecuteAsync(_stoppingCts.Token);
// If the task is completed then return it,
// this will bubble cancellation and failure to the caller
if (_executingTask.IsCompleted)
{
return _executingTask;
}
// Otherwise it's running
return Task.CompletedTask;
}
public virtual async Task StopAsync(CancellationToken cancellationToken)
{
// Stop called without start
if (_executingTask == null)
{
return;
}
try
{
// Signal cancellation to the executing method
_stoppingCts.Cancel();
}
finally
{
// Wait until the task completes or the stop token triggers
await Task.WhenAny(_executingTask, Task.Delay(Timeout.Infinite, cancellationToken));
}
}
public virtual void Dispose()
{
_stoppingCts.Cancel();
}
}
實(shí)現(xiàn)一個(gè)簡(jiǎn)單的后臺(tái)定時(shí)任務(wù)
基于上面的信息,我們可以基于 IHostedService 實(shí)現(xiàn)一個(gè)簡(jiǎn)單的后臺(tái)定時(shí)任務(wù)服務(wù),
public abstract class ScheduedService : IHostedService, IDisposable
{
private readonly Timer _timer;
private readonly TimeSpan _period;
protected readonly ILogger Logger;
protected ScheduedService(TimeSpan period, ILogger logger)
{
Logger = logger;
_period = period;
_timer = new Timer(Execute, null, Timeout.Infinite, 0);
}
public void Execute(object state = null)
{
try
{
Logger.LogInformation("Begin execute service");
ExecuteAsync().Wait();
}
catch (Exception ex)
{
Logger.LogError(ex, "Execute exception");
}
finally
{
Logger.LogInformation("Execute finished");
}
}
protected abstract Task ExecuteAsync();
public virtual void Dispose()
{
_timer?.Dispose();
}
public Task StartAsync(CancellationToken cancellationToken)
{
Logger.LogInformation("Service is starting.");
_timer.Change(TimeSpan.FromSeconds(SecurityHelper.Random.Next(10)), _period);
return Task.CompletedTask;
}
public Task StopAsync(CancellationToken cancellationToken)
{
Logger.LogInformation("Service is stopping.");
_timer?.Change(Timeout.Infinite, 0);
return Task.CompletedTask;
}
}
基于上面這個(gè)基于Timer實(shí)現(xiàn)的后臺(tái)定時(shí)任務(wù)類(lèi)實(shí)現(xiàn)一個(gè)定時(shí)任務(wù):
public class RemoveOverduedReservtaionService : ScheduedService
{
public RemoveOverduedReservtaionService(ILogger<RemoveOverduedReservtaionService> logger) : base(TimeSpan.FromDays(1), logger)
{ }
protected override Task ExecuteAsync()
{
return DependencyResolver.Current.TryInvokeServiceAsync<IEFRepository<ReservationDbContext, Reservation>>(reservationRepo =>
{
return reservationRepo.DeleteAsync(reservation => reservation.ReservationStatus == 0 && (reservation.ReservationForDate < DateTime.Today.AddDays(-3)));
});
}
}
這個(gè)類(lèi)實(shí)現(xiàn)的是每天執(zhí)行一次,刪除三天前狀態(tài)為待審核的預(yù)約,完整實(shí)現(xiàn)代碼:https://github.com/WeihanLi/ActivityReservation/blob/dev/ActivityReservation.Helper/Services/RemoveOverduedReservtaionService.cs
執(zhí)行日志:
通過(guò)日志可以看到我們的定時(shí)任務(wù)確實(shí)是每天執(zhí)行一次,這樣我們的定時(shí)任務(wù)就算是簡(jiǎn)單的完成了。
Reference
https://docs.microsoft.com/en-us/aspnet/core/fundamentals/host/hosted-services
https://docs.microsoft.com/en-us/dotnet/standard/microservices-architecture/multi-container-microservice-net-applications/background-tasks-with-ihostedservice
https://docs.microsoft.com/zh-cn/dotnet/standard/microservices-architecture/multi-container-microservice-net-applications/background-tasks-with-ihostedservice
https://www.stevejgordon.co.uk/asp-net-core-2-ihostedservice
https://github.com/WeihanLi/ActivityReservation
總結(jié)
以上是生活随笔為你收集整理的.net core 基于 IHostedService 实现定时任务的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 架构杂谈《八》Docker 架构
- 下一篇: C#中谁最快:结构还是类?