AspNet Core 结合 Quartz 使用定时任务且通过注入缓存或者配置参数
一, 经常在项目会用到定时任务同步数据或更新缓存等操作, 在很久以前我们可能经常会用一个多线程或 timer 来做定时任务, 这样能实现比较简单轻量级的任务; 对于任务多且都调用频率不一样的任务, 我们都会用到 Quartz.NET 这个组件;
Quartz.NET 是一个强大, 开源, 轻量的作业调度框架, 你能够用它来为执行一个作业而创建简单的或复杂的作业调度. 它有很多特征, 如: 数据库支持, 集群, 插件, 支持 cron-like 表达式等等
二, 接下来简单演示一下 Quartz 使用:
2.1 首先新建一个 AspNet Core API 项目, 通过 nuget 包管理器安装引用 Quartz
<喎"/kf/ware/vc/" target="_blank" class="keylink">vcD4KPHA+ICAgICAyLjIg0MK9qNK7uPbEo8TiyM7O8cDgVXNlckluZm9TeW5jam9iILHY0Ou8zLPQSUpvYr3Tv9o8L3A+Cgo8cHJlIGNsYXNzPQ=="brush:java;">namespace QuartzDemo.Quarzs { public class UserInfoSyncjob : IJob { public Task Execute(IJobExecutionContext context) { return Task.Run(() => { //..... Console.WriteLine($"{DateTime.Now.ToString()}: 开始执行同步第三方数据"); //.... 同步操作 }); } } }
2.2 声明一个启动类 QuartzStartup, 来控制任务启动关闭等方法
添加启动方法
- public async Task Start()
- {
- //1, 声明一个调度工厂
- _schedulerFactory = new StdSchedulerFactory();
- //2, 通过调度工厂获得调度器
- _scheduler = await _schedulerFactory.GetScheduler();
- //3, 开启调度器
- await _scheduler.Start();
- //4, 创建一个触发器
- var trigger = TriggerBuilder.Create()
- .WithSimpleSchedule(x => x.WithIntervalInSeconds(2).RepeatForever())// 每两秒执行一次
- .Build();
- //5, 创建任务
- var jobDetail = JobBuilder.Create()
- .WithIdentity("job", "group")
- .Build();
- //6, 将触发器和任务器绑定到调度器中
- await _scheduler.ScheduleJob(jobDetail, trigger);
- return await Task.FromResult("将触发器和任务器绑定到调度器中完成");
- }
2.3 在网站启动完成时调用 QuartzStartup 的 Start 方法开启任务
先注入 Quartz 调度类
添加网站启动开始方法
2.4, 运行效果, 运行之前将控制台开启 (方便查看任务是否在执行, 实际环境可写日志)
该调度任务完成, 上方定义的触发器是 2 秒一次, 所以该任务每隔 2 秒执行;(也可以通过配置文件, 控制执行平率, cron 表达式可以很好控制)
三, 第二结简单演示了 Quartz 的基本用法, 本文重点不是主要讲解 Quartz 的用法, 上方只是为了没使用过 Quartz 的同行有个简单映像, 如果想详细学习, 博客园有很多类似的文章, 也可以和我探讨一下!
本文重点是每个任务类怎么通过注入获取其他类的使用及参数配置类等等;
假如有这样一个需求, UserInfoSyncjob 同步任务里面需要配置数据库连接参数和日志记录, 缓存记录等, 在之前我们可能通过配置类, 日志类, 缓存类以工厂形式单例创建获取.
在 AspNet Core 自带 IoC 容器框架, 很多配置类, 日志类, 缓存类等等, 在全局很多地方都会使用, 我们现在做法就是把这些类注入到 IoC 容器中, 如果需要的只需要从构造方法中获取;
我们都知道如果一个从构造方法中获取 IoC 容器里面的类型实例, 必须该类型也要主要到 IoC 容器中, 这样我们就要想办法把 UserInfoSyncjob 通过容器来创建生产;
通过源码发现在 Quartz 有一个默认的生成 job 的工厂类 Quartz.Simpl.SimpleJobFactory
- using System;
- using Quartz.Logging;
- using Quartz.Spi;
- using Quartz.Util;
- namespace Quartz.Simpl
- {
- ///
- /// The default JobFactory used by Quartz - simply calls
- ///
- ///
- /// Marko Lahma (.NET)
- public class SimpleJobFactory : IJobFactory
- {
- private static readonly ILog log = LogProvider.GetLogger(typeof (SimpleJobFactory));
- ///
- /// Called by the scheduler at the time of the trigger firing, in order to
- /// produce a
- ///
- /// 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
- ///
- /// The TriggerFiredBundle from which the
- ///
- /// the newly instantiated Job
- /// SchedulerException if there is a problem instantiating the Job.
- 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(jobType);
- }
- catch (Exception e)
- {
- SchedulerException se = new SchedulerException($"Problem instantiating class'{jobDetail.JobType.FullName}'", e);
- throw se;
- }
- }
- ///
- /// Allows the job factory to destroy/cleanup the job if needed.
- /// No-op when using SimpleJobFactory.
- ///
- public virtual void ReturnJob(IJob job)
- {
- var disposable = job as IDisposable;
- disposable?.Dispose();
- }
- }
- }
SimpleJobFactory 实现了 IJobFactory 接口, 通过源码发现我们如果要替换该工厂来控制 job 的生成, 只需要创建一个 IOCJobFactory 来替换默认 job 工厂就行
- public class IOCJobFactory : IJobFactory
- {
- private readonly IServiceProvider _serviceProvider;
- public IOCJobFactory(IServiceProvider serviceProvider)
- {
- _serviceProvider = serviceProvider;
- }
- public IJob NewJob(TriggerFiredBundle bundle, IScheduler scheduler)
- {
- return _serviceProvider.GetService(bundle.JobDetail.JobType) as IJob;
- }
- public void ReturnJob(IJob job)
- {
- var disposable = job as IDisposable;
- disposable?.Dispose();
- }
- }
在调度任务类里面重新设置 job 工厂 _scheduler.JobFactory = _iocJobfactory;
在 IoC 中注入 UserInfoSyncjob,StdSchedulerFactory,IOCJobFactory
- services.AddTransient(); // 这里使用瞬时依赖注入
- services.AddSingleton();// 注册 ISchedulerFactory 的实例.
- services.AddSingleton();
- services.AddSingleton();
修改 UserInfoSyncjob 任务类, 可以通过构造方法注入的方式从容器中拿到日志实现类, 缓存类等等
- public class UserInfoSyncjob : IJob
- {
- private readonly ILogger _logger;
- // private readonly ICache _cache;
- public UserInfoSyncjob(ILogger logger)
- {
- //_cache = cache;
- _logger = logger;// EnginContext.Current.Resolve>();
- }
- public Task Execute(IJobExecutionContext context)
- {
- return Task.Run(() =>
- {
- //.....
- // Console.WriteLine($"{DateTime.Now.ToString()}: 开始执行同步第三方数据");
- _logger.LogInformation ($"{DateTime.Now.ToString()}: 开始执行同步第三方数据");
- //.... 同步操作
- // 我们都知道如果一个从构造方法中获取 IoC 容器里面的类型, 必须该类型也要主要到 IoC 容器中;
- });
- }
- }
调整后运行截图
具体详细步骤请看源码: https://github.com/lxshwyan/QuartzDemo.git
来源: https://www.2cto.com/kf/201905/808800.html