.net core 基于 IHostedService 实现定时任务

640?wx_fmt=png

.net core 基于 IHostedService 实现定时任务

Intro

从 .net core 2.0 开始,开始引入 IHostedService,可以通过 IHostedService 来实现后台任务,但是只能在 WebHost 的基础上使用。从 .net core 2.1 开始微软引入通用主机( GenericHost),使得我们可以在不使用 Web 的情况下,也可以使用 IHostedService 来实现 定时任务/Windows服务/后台任务,并且引入了一个 BackgroundService 抽象类来更方便的创建后台任务。

IHostedService

IHostedService 后台任务的执行与应用程序(就此而言,为主机或微服务)的生存期相协调。当应用程序启动时注册任务,当应用程序关闭时,有机会执行某些正常操作或清理。

始终可以启动后台线程来运行任何任务,而无需使用 IHostedService。不同之处就在于应用的关闭时间,此时会直接终止线程,而没有机会执行正常的清理操作。

当注册 IHostedService 时,.NET Core 会在应用程序启动和停止期间分别调用 IHostedService 类型的 StartAsync()StopAsync() 方法。具体而言,即在服务器已启动并已触发 IApplicationLifetime.ApplicationStarted 后调用 start。

 
 
  1. namespace Microsoft.Extensions.Hosting

  2. {

  3. //

  4. // Summary:

  5. // Defines methods for objects that are managed by the host.

  6. public interface IHostedService

  7. {

  8. //

  9. // Summary:

  10. // Triggered when the application host is ready to start the service.

  11. Task StartAsync(CancellationToken cancellationToken);

  12. //

  13. // Summary:

  14. // Triggered when the application host is performing a graceful shutdown.

  15. Task StopAsync(CancellationToken cancellationToken);

  16. }

  17. }

可以从头开始创建自定义托管服务类并实现 IHostedService,因为在使用 .NET Core 2.0 时需执行这些操作。

但是,由于大多数后台任务在取消令牌管理和其他典型操作方面都有类似的需求,因此 .net core 2.1 有一个非常方便且可以从中进行派生的抽象基类, BackgroundService 定义如下:

 
 
  1. // Copyright (c) .NET Foundation. Licensed under the Apache License, Version 2.0.

  2. /// <summary>

  3. /// Base class for implementing a long running <see cref="IHostedService"/>.

  4. /// </summary>

  5. public abstract class BackgroundService : IHostedService, IDisposable

  6. {

  7. private Task _executingTask;

  8. private readonly CancellationTokenSource _stoppingCts =

  9. new CancellationTokenSource();


  10. protected abstract Task ExecuteAsync(CancellationToken stoppingToken);


  11. public virtual Task StartAsync(CancellationToken cancellationToken)

  12. {

  13. // Store the task we're executing

  14. _executingTask = ExecuteAsync(_stoppingCts.Token);


  15. // If the task is completed then return it,

  16. // this will bubble cancellation and failure to the caller

  17. if (_executingTask.IsCompleted)

  18. {

  19. return _executingTask;

  20. }


  21. // Otherwise it's running

  22. return Task.CompletedTask;

  23. }


  24. public virtual async Task StopAsync(CancellationToken cancellationToken)

  25. {

  26. // Stop called without start

  27. if (_executingTask == null)

  28. {

  29. return;

  30. }


  31. try

  32. {

  33. // Signal cancellation to the executing method

  34. _stoppingCts.Cancel();

  35. }

  36. finally

  37. {

  38. // Wait until the task completes or the stop token triggers

  39. await Task.WhenAny(_executingTask, Task.Delay(Timeout.Infinite, cancellationToken));

  40. }


  41. }


  42. public virtual void Dispose()

  43. {

  44. _stoppingCts.Cancel();

  45. }

  46. }

640?wx_fmt=png

实现一个简单的后台定时任务

基于上面的信息,我们可以基于 IHostedService 实现一个简单的后台定时任务服务,

 
 
  1. public abstract class ScheduedService : IHostedService, IDisposable

  2. {

  3. private readonly Timer _timer;

  4. private readonly TimeSpan _period;

  5. protected readonly ILogger Logger;


  6. protected ScheduedService(TimeSpan period, ILogger logger)

  7. {

  8. Logger = logger;

  9. _period = period;

  10. _timer = new Timer(Execute, null, Timeout.Infinite, 0);

  11. }


  12. public void Execute(object state = null)

  13. {

  14. try

  15. {

  16. Logger.LogInformation("Begin execute service");

  17. ExecuteAsync().Wait();

  18. }

  19. catch (Exception ex)

  20. {

  21. Logger.LogError(ex, "Execute exception");

  22. }

  23. finally

  24. {

  25. Logger.LogInformation("Execute finished");

  26. }

  27. }


  28. protected abstract Task ExecuteAsync();


  29. public virtual void Dispose()

  30. {

  31. _timer?.Dispose();

  32. }


  33. public Task StartAsync(CancellationToken cancellationToken)

  34. {

  35. Logger.LogInformation("Service is starting.");

  36. _timer.Change(TimeSpan.FromSeconds(SecurityHelper.Random.Next(10)), _period);

  37. return Task.CompletedTask;

  38. }


  39. public Task StopAsync(CancellationToken cancellationToken)

  40. {

  41. Logger.LogInformation("Service is stopping.");


  42. _timer?.Change(Timeout.Infinite, 0);


  43. return Task.CompletedTask;

  44. }

  45. }

基于上面这个基于Timer实现的后台定时任务类实现一个定时任务:

 
 
  1. public class RemoveOverduedReservtaionService : ScheduedService

  2. {

  3. public RemoveOverduedReservtaionService(ILogger<RemoveOverduedReservtaionService> logger) : base(TimeSpan.FromDays(1), logger)

  4. { }


  5. protected override Task ExecuteAsync()

  6. {

  7. return DependencyResolver.Current.TryInvokeServiceAsync<IEFRepository<ReservationDbContext, Reservation>>(reservationRepo =>

  8. {

  9. return reservationRepo.DeleteAsync(reservation => reservation.ReservationStatus == 0 && (reservation.ReservationForDate < DateTime.Today.AddDays(-3)));

  10. });

  11. }

  12. }

这个类实现的是每天执行一次,删除三天前状态为待审核的预约,完整实现代码:https://github.com/WeihanLi/ActivityReservation/blob/dev/ActivityReservation.Helper/Services/RemoveOverduedReservtaionService.cs

执行日志:

640?wx_fmt=png

通过日志可以看到我们的定时任务确实是每天执行一次,这样我们的定时任务就算是简单的完成了。

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


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值