在线时间:8:00-16:00
迪恩网络APP
随时随地掌握行业动态
扫描二维码
关注迪恩网络微信公众号
介绍Quartz.Net是一个强大、开源、轻量的作业调度框架。在平时开发中主要用于一些定时任务开发,譬如定时发送右键,定时同步第三方数据等等。 github:https://github.com/quartznet/quartznet 官网:https://www.quartz-scheduler.net/documentation/quartz-3.x/quick-start.html 目标这次博客主要实现任务的启动,暂停,恢复。 封装为了项目的低耦合,在封装Quartz的时候将它单独封装在一个类库中,便于管理。 先创建一个类库,定义好类库名字。 安装nuget包。 Job工厂在quartz中,在实现Job的时候会实例化一个JobFactory,翻译过来就是Job工厂,通过查看源码我找到了一个SimpleJobFactory,这是它的默认实现,它做的事情主要是实例化实现IJob的类。 那么自定义一个自己适用的Job工厂。 在这里的想法是,在IOC中放入Job,然后再工厂中获取容器中的Job对象。事实上,你实现工厂的核心就是定义IJob实现类的实例化规则! 因为我需要对.net core ioc进行操作,所以安装一个Microsoft.Extensions.DependencyInjection nuget包 代码: /// <summary> /// 自定义job工厂 /// https://www.quartz-scheduler.net/documentation/quartz-3.x/tutorial/miscellaneous-features.html#plug-ins /// </summary> public class ZeroJobFactory : IJobFactory { readonly IServiceProvider _provider; public ZeroJobFactory(IServiceProvider provider) { _provider = provider; } public IJob NewJob(TriggerFiredBundle bundle, IScheduler scheduler) { try { //从Quartz.net的源码实现net core注入这一块能够发现,job实例是通过AddTransient加入容器中的 //还有自定义的JobFactory也需要单例注入,我觉的是因为如果不单例注入会导致Quartz使用默认的SimpleJobFactory //从而导致这里的获取Job实例出问题。 var service = _provider.CreateScope(); var job = service.ServiceProvider.GetService(bundle.JobDetail.JobType) as IJob; return job; } catch (Exception ex) { throw ex; } } /// <summary> /// 允许工厂释放Job /// </summary> /// <param name="job"></param> public void ReturnJob(IJob job) { var disposable = job as IDisposable; disposable?.Dispose(); } 创建Job这个类必须要继承IJob,否则是没用的。 [DisallowConcurrentExecution] [PersistJobDataAfterExecution] public class FirstJob : IJob { public async Task Execute(IJobExecutionContext context) { await Task.Run(() => { Console.WriteLine("First Job"); //job详情 var jobDetails = context.JobDetail; //触发器的信息 var trigger = context.Trigger; Console.WriteLine($"JobKey:{jobDetails.Key},Group:{jobDetails.Key.Group}\r\n" + $"Trigger:{trigger.Key}\r\n" + $"RunTime:{context.JobRunTime}" + $"ExecuteTime:{DateTime.Now}"); }); } } 控制中心在自定义Job工厂和Job之后,那如何统一控制呢,这里说到控制中心,在控制中心里实现Job的运行,暂停,恢复等功能。 首先定义一个抽象的任务调度的控制中心,在.net core大多数时候都是面向抽象编程,所以这个控制中心定义为抽象的,便于依赖注入从而能够方便的在API中进行任务调度。 /// <summary> /// quartz 抽象调度中心 /// </summary> public interface IControllerCenter { /// <summary> /// 启动任务调度 /// </summary> /// <returns></returns> Task Start(); /// <summary> /// 运行Job /// </summary> /// <returns></returns> Task RunJob(); /// <summary> /// 暂停Job /// </summary> /// <returns></returns> Task PauseJob(); /// <summary> /// 回复job /// </summary> /// <returns></returns> Task ResumeJob(); } 实现任务中心 /// <summary> /// Quartz任务调度控制中心 /// </summary> public class ControllerCenter : IControllerCenter { /// <summary> /// 构造函数注入自定义Job工厂 /// </summary> readonly IJobFactory _jobFactory; private Task<IScheduler> _scheduler; public Task<IScheduler> Center => _scheduler ?? throw new ArgumentNullException("Schedule can not null"); private IScheduler Scheduler => _scheduler.Result; public ControllerCenter(IJobFactory factory) { _jobFactory = factory; _scheduler = GetScheduler(); _scheduler.Result.JobFactory = _jobFactory; } private Task<IScheduler> GetScheduler() { if (_scheduler != null) return _scheduler; else { /* * 配置二进制策略 *https://www.quartz-scheduler.net/documentation/quartz-3.x/migration-guide.html#packaging-changes */ var properties = new NameValueCollection { ["quartz.serializer.type"] = "binary" }; //实例化工厂 ISchedulerFactory sf = new StdSchedulerFactory(properties); this._scheduler = sf.GetScheduler(); return _scheduler; } } public async Task Start() { try { if (this.Scheduler.IsStarted) { Console.WriteLine("quartz is started"); } else { Console.WriteLine("quartz start!"); await Scheduler.Start(); } } catch (Exception ex) { Console.WriteLine(ex.Message); } } public async Task RunJob() { var jobKey = new JobKey("job1", "group1"); if (await Scheduler.CheckExists(jobKey)) { Console.WriteLine("JobKey Exists"); } else { Console.WriteLine("JobKey Allow"); if (!this.Scheduler.IsStarted) { Console.WriteLine("quartz is not started"); await this.Start(); } var job = JobBuilder.Create<FirstJob>() .WithIdentity("job1", "group1") .Build(); var trigger = TriggerBuilder.Create() .WithIdentity("trigger1", "group1") .StartNow() .WithSimpleSchedule(a => { a.RepeatForever();//永远执行 a.WithIntervalInSeconds(3);//执行间隔3s }) .ForJob(job) .Build(); await Scheduler.ScheduleJob(job, trigger); } } public async Task PauseJob() { var jobKey = new JobKey("job1", "group1"); if (await Scheduler.CheckExists(jobKey)) { await Scheduler.PauseJob(jobKey); Console.WriteLine("PauseJob Success!"); } else { Console.WriteLine("Not IsExists JobKey"); } } public async Task ResumeJob() { var jobKey = new JobKey("job1", "group1"); if (await Scheduler.CheckExists(jobKey)) { await Scheduler.ResumeJob(jobKey); Console.WriteLine("ResumeJob Success!"); } else { Console.WriteLine("Not IsExists JobKey"); } } 可以看到我在RunJob中已经将创建的Job放入,接下来就要说到如何通过控制中心去使用了。 使用在.net core Startup中将自定义工厂,控制中心放入容器。 //自定义Job工厂 services.AddSingleton<IJobFactory, ZeroJobFactory>(); //任务调度控制中心 services.AddSingleton<IControllerCenter, ControllerCenter>(); //Jobs,将组件好的Job放在这里,生命周期为瞬时的 services.AddTransient<FirstJob>(); 创建一个控制器,直接上代码 [Route("api/[controller]")] [ApiController] public class QuartzController : ControllerBase { readonly IControllerCenter _center; public QuartzController(IControllerCenter center) { _center = center; } /// <summary> /// 开启任务调度 /// </summary> /// <returns></returns> [HttpGet("Start")] public async Task<JsonResult> Start() { await _center.Start(); return AjaxHelper.Seed(Ajax.Ok); } /// <summary> /// 运行Job /// </summary> /// <returns></returns> [HttpGet("Run")] public async Task<JsonResult> Run() { await _center.RunJob(); return AjaxHelper.Seed(Ajax.Ok); } /// <summary> /// 暂停Job /// </summary> /// <returns></returns> [HttpGet("PauseJob")] public async Task<JsonResult> PauseJob() { await _center.PauseJob(); return AjaxHelper.Seed(Ajax.Ok); } /// <summary> /// 恢复Job /// </summary> /// <returns></returns> [HttpGet("ResumeJob")] public async Task<JsonResult> ResumeJob() { await _center.ResumeJob(); return AjaxHelper.Seed(Ajax.Ok); } } 代码写好,就来测试一下。 测试首先测试启动任务调度和运行Job 注意看控制台的打印的信息,跟我们在Job中定义的内容是一样的,而且我在触发器中指定了没3s执行一次,可以从执行时间看到是实现了的。 既然任务启动了,那我就试着将它暂停下吧。 可以从打印信息看出,我在暂停之后又将它恢复使用。这里需要注意的一点是,如果Job没有启动是没法根据JobKey去暂停的。 总结今天对Quartz结合.net core进行了简单的配合使用。完成了博客开头说到的目标:启动,暂停,恢复。 总的来说Quartz是一个非常好玩的东西的,但是我在动手之前是花了近乎一周的时间去看文档,看代码,看优秀.neter对它的使用方式。 在了解到足够多的信息才去动手做的。这一点的做法对我的帮助是非常大,在做的过程中遇到的阻碍相较来说少了很多。 源码地址:https://github.com/QQ2287991080/Zero.Core
|
请发表评论