AspNet Core实现web定时任务

作为一枚后端程序狗,项目实践常遇到定时任务的工作,最容易想到的的思路就是利用Windows计划任务/wndowsservice程序/Crontab程序等主机方法在主机上部署定时任务程序/脚本。但是很多时候,若使用的是共享主机或者受控主机,这些主机不允许你私自安装exe程序、Windows服务程序。码甲会想到在web程序中做定时任务,目前有两个方向:①.AspNetCore自带的HostServic...

AspNet Core实现web定时任务

作为一枚后端程序狗,项目实践常遇到定时任务的工作,最容易想到的的思路就是利用Windows计划任务/wndows service程序/Crontab程序等主机方法在主机上部署定时任务程序/脚本。

但是很多时候,若使用的是共享主机或者受控主机,这些主机不允许你私自安装exe程序、Windows服务程序。

码甲会想到在web程序中做定时任务, 目前有两个方向:

①.AspNetCore自带的HostService, 这是一个轻量级的后台服务, 需要搭配timer完成定时任务

②.老牌Quartz.Net组件,支持复杂灵活的Scheduling、支持ADO/RAM Job任务存储、支持集群、支持监听、支持插件。

此处我们的项目使用稍复杂的Quartz.net实现web定时任务。

项目背景

最近需要做一个计数程序:采用redis计数,设定每小时将当日累积数据持久化到关系型数据库sqlite。

添加Quartz.Net Nuget 依赖包:<PackageReference Include="Quartz" Version="3.0.6" />

①.定义定时任务内容: Job

②.设置触发条件: Trigger

③.将Quartz.Net集成进AspNet Core

头脑风暴

IScheduler类包装了上述背景需要完成的第①②点工作 ,SimpleJobFactory定义了生成指定的Job任务的过程,这个行为是利用反射机制调用无参构造函数构造出的Job实例。下面是源码:

//----------------选自Quartz.Simpl.SimpleJobFactory类-------------using System;using Quartz.Logging;using Quartz.Spi;using Quartz.Util;namespace Quartz.Simpl{ /// <summary>  /// The default JobFactory used by Quartz - simply calls  /// <see cref="ObjectUtils.InstantiateType{T}" /> on the job class. /// </summary> /// <seealso cref="IJobFactory" /> /// <seealso cref="PropertySettingJobFactory" /> /// <author>James House</author> /// <author>Marko Lahma (.NET)</author> public class SimpleJobFactory : IJobFactory {  private static readonly ILog log = LogProvider.GetLogger(typeof (SimpleJobFactory));  /// <summary>  /// Called by the scheduler at the time of the trigger firing, in order to  /// produce a <see cref="IJob" /> instance on which to call Execute.  /// </summary>  /// <remarks>  /// It should be extremely rare for this method to throw an exception -  /// basically only the case where there is no way at all to instantiate  /// and prepare the Job for execution.  When the exception is thrown, the  /// Scheduler will move all triggers associated with the Job into the  /// <see cref="TriggerState.Error" /> state, which will require human  /// intervention (e.g. an application restart after fixing whatever  /// configuration problem led to the issue with instantiating the Job).  /// </remarks>  /// <param name="bundle">The TriggerFiredBundle from which the <see cref="IJobDetail" />  ///and other info relating to the trigger firing can be obtained.</param>  /// <param name="scheduler"></param>  /// <returns>the newly instantiated Job</returns>  /// <throws>  SchedulerException if there is a problem instantiating the Job. </throws>  public virtual IJob NewJob(TriggerFiredBundle bundle, IScheduler scheduler)  {IJobDetail jobDetail = bundle.JobDetail;Type jobType = jobDetail.JobType;try{ if (log.IsDebugEnabled()) {  log.Debug($"Producing instance of Job '{jobDetail.Key}', class={jobType.FullName}"); } return ObjectUtils.InstantiateType<IJob>(jobType);}catch (Exception e){ SchedulerException se = new SchedulerException($"Problem instantiating class '{jobDetail.JobType.FullName}'", e); throw se;}  }  /// <summary>  /// Allows the job factory to destroy/cleanup the job if needed.   /// No-op when using SimpleJobFactory.  /// </summary>  public virtual void ReturnJob(IJob job)  {var disposable = job as IDisposable;disposable?.Dispose();  } }}//------------------节选自Quartz.Util.ObjectUtils类------------------------- public static T InstantiateType<T>(Type type){  if (type == null)  { throw new ArgumentNullException(nameof(type), "Cannot instantiate null");  }  ConstructorInfo ci = type.GetConstructor(Type.EmptyTypes);  if (ci == null)  { throw new ArgumentException("Cannot instantiate type which has no empty constructor", type.Name);  }  return (T) ci.Invoke(new object[0]);}

很多时候,定义的Job任务依赖了其他组件,这时默认的SimpleJobFactory不可用, 需要考虑将Job任务作为依赖注入组件,加入依赖注入容器。

关键思路:

①. IScheduler 开放了JobFactory 属性,便于你控制Job任务的实例化方式;

JobFactories may be of use to those wishing to have their application produce IJob instances via some special mechanism, such as to give the opportunity for dependency injection

②. AspNet Core的服务架构是以依赖注入为基础的,利用AspNet Core已有的依赖注入容器IServiceProvider管理Job 服务的创建过程。

编码实践

① 定义Job内容:

// -------每小时将redis数据持久化到sqlite, 每日凌晨跳针,持久化昨天全天数据---------------------public class UsageCounterSyncJob : IJob{  private readonly EqidDbContext _context;  private readonly IDatabase _redisDB1;  private readonly ILogger _logger;  public UsageCounterSyncJob(EqidDbContext context, RedisDatabase redisCache, ILoggerFactory loggerFactory)  {_context = context
源文地址:https://www.guoxiongfei.cn/cntech/9859.html