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


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

    Intro

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

    IHostedService

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

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

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

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

    但是,由于大多数后台任务在取消令牌管理和其他典型操作方面都有类似的需求,因此 .net core 2.1 有一个非常方便且可以从中进行派生的抽象基类,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();
        }
    }
    

    实现一个的后台定时任务

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

    public abstract class ScheduledService : IHostedService, IDisposable
    {
        private readonly Timer _timer;
        private readonly TimeSpan _period;
        protected readonly ILogger Logger;
    
        protected ScheduledService(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;
        }
    }
    

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

    public class RemoveOverdueReservationService : ScheduledService
    {
        public RemoveOverdueReservationService(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)));
            });
        }
    }
    

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

    在程序启动的时候注册服务:

    services.AddHostedService<RemoveOverduedReservtaionService>();
    

    执行日志:

    IHostedServiceBasedScheduledService Logs

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

    Reference

  • 相关阅读:
    采用坐标变换(移动和旋转)画箭头
    学会Func
    一个链接器的实现
    linux内核skb操作
    终于实现samba可写不可删除
    删掉SafeDrv病毒(这个病毒有点意思)
    Writing a ServiceMain Function(使用RegisterServiceCtrlHandler函数)
    利用Winscp,Putty实现Windows下编写Linux程序
    联发科6亿美元将大陆子公司卖给四维图新(180个人价值6亿美元)
    TFTP:简单文本传输协议,BOOTP:引导程序协议
  • 原文地址:https://www.cnblogs.com/weihanli/p/implement-timed-background-service-with-ihostedservice.html
Copyright © 2020-2023  润新知