无需Get更多技能,快速打造一个可持久化的任务调度
来源:互联网 发布:php后台admin 编辑:程序博客网 时间:2024/06/05 14:44
项目总是很忙,忙里偷闲还是要总结一下,前一段时间,由于项目中需要,我们需要很多定时汇总数据的情况,项目初期主要使用sql server 计划任务实现对数据的汇总与统计,但是开发到一定时间内,需求提出了很多自动任务的功能,很多不是能够在SQL Server中进行解决的,例如订单关闭前多少分钟内发短信与邮箱告诉用户,程序层面每天汇总错误日志发送至运维邮箱等,针对目前情况,简单上网查了一下,决定使用TopShelf + Quartz 进行自动服务与任务调度,关于这两项的介绍也很多了,废话不多说,直接上代码:
项目结构图如下:
任务调度与任务实现实质是两个完全不同的东西,所以自动服务于Job执行是应该分开的,所以任务创建时应该吧Job加载到任务调度工程,最后告诉调度工厂,可以开始执行任务之后,调度工厂会进行扫描Job,加载到内存中挨个执行。
IAutoService: 主要是声明自动服务的接口模式,标记了服务的名称,启动时的加载等,统一由抽象类AutoServcieBase实现。
1 public interface IAutoService 2 { 3 /// <summary> 4 /// 服务名称 5 /// </summary> 6 string ServiceName { get; } 7 8 /// <summary> 9 /// 启动方法10 /// </summary>11 void Start();12 13 /// <summary>14 /// 是否启用15 /// </summary>16 bool IsEnable { get; }17 }
AutoServiceBase: 抽象类实现接口原则,规范自动任务的可选参数,统一封装调用,定义抽象方法与属性,子类重写。
1 public abstract class AutoServiceBase : IAutoService 2 { 3 public IQuartzConfiguration QuartzConfiguration { get; set; } 4 5 public IQuartzScheduleJobManager QuartzScheduleJobManager { get; set; } 6 7 public const string ServiceGroupName = "AutoService.Groups"; 8 9 /// <summary>10 /// 服务名称11 /// </summary>12 public virtual string ServiceName { get { return "服务启动..."; } }13 14 /// <summary>15 /// 任务调度16 /// </summary>17 public virtual Action<TriggerBuilder> ConfigureTrigger { get; }18 19 public virtual bool IsEnable { get { return true; } }20 21 /// <summary>22 /// 获取服务方法名称23 /// </summary>24 /// <returns></returns>25 public abstract string GetServiceIdentiy();26 27 /// <summary>28 /// 启动任务29 /// </summary>30 public abstract void Start();31 32 }
定义好基础服务后,接下来我们就要使用TopShelf搭建一个属于我们自己的windows service了,代码如下:
class Program { static void Main(string[] args) { HostFactory.Run(x => { x.Service<Service>(s => { s.ConstructUsing(name => new Service()); s.WhenStarted(tc => tc.Start()); s.WhenStopped(tc => tc.Stop()); s.WhenShutdown(tc => tc.Shutdown()); }); x.RunAsLocalSystem(); x.SetDescription("服务集合,包含自动化消息,响应队列的领域事件"); x.SetDisplayName("Test.Services"); x.SetServiceName("Test.Services"); }); } }
topshelf是一个很简单就能创建service的工具,它开源在GitHub TopShelf上,是一款快速搭建mono与windows service ,在编写好我们的服务代码,只需简单地启动代码,就能创建一个持久化的服务,不过想要兼容在Mono上也是非常简单,只需加入如下一句代码即可。
x.UseLinuxIfAvailable();
在我们的项目中windows service不仅仅是任务调度的寄宿形式,同时也是我们Event Store,对DDD感兴趣的同学可以了解CQRS中SAGA模式中事件消费的形式,这个不在本文讨论。
创建好我们的基础服务后,接下来就是对任务调度工厂的设置,任务调度简单来看,我们往往要使用事件监听,任务总调度工厂等等,这里Quartz很好的集成了这些,我们只需进行简单的改造就能够实现如上功能。
QuartzScheduleJobManager:实现任务调度管理,在这里可以对总任务调度工厂进行启动,停止,加载Job等
1 public interface IQuartzScheduleJobManager 2 { 3 /// <summary> 4 /// 增加任务与任务调度到任务工厂 5 /// </summary> 6 /// <typeparam name="TJob"></typeparam> 7 /// <param name="configureJob">任务</param> 8 /// <param name="configureTrigger">任务调度</param> 9 /// <returns></returns>10 Task ScheduleAsync<TJob>(Action<JobBuilder> configureJob, Action<TriggerBuilder> configureTrigger) where TJob : IJob;11 12 /// <summary>13 /// 启动任务14 /// </summary>15 void Start();16 17 /// <summary>18 /// 等待任务完成并停止任务19 /// </summary>20 void ShutDown();21 }22 23 public class QuartzScheduleJobManager : IQuartzScheduleJobManager24 {25 private readonly IQuartzConfiguration _quartzConfiguration;26 27 public QuartzScheduleJobManager(28 IQuartzConfiguration quartzConfiguration)29 {30 _quartzConfiguration = quartzConfiguration;31 }32 33 /// <summary>34 /// 增加任务与任务调度到任务工厂35 /// </summary>36 /// <typeparam name="TJob"></typeparam>37 /// <param name="configureJob">任务</param>38 /// <param name="configureTrigger">任务调度</param>39 /// <returns></returns>40 public Task ScheduleAsync<TJob>(Action<JobBuilder> configureJob, Action<TriggerBuilder> configureTrigger)41 where TJob : IJob42 {43 var jobToBuild = JobBuilder.Create<TJob>();44 configureJob(jobToBuild);45 var job = jobToBuild.Build();46 47 var triggerToBuild = TriggerBuilder.Create();48 configureTrigger(triggerToBuild);49 var trigger = triggerToBuild.Build();50 51 _quartzConfiguration.Scheduler.ScheduleJob(job, trigger);52 53 return Task.FromResult(0);54 }55 56 /// <summary>57 /// 启动任务58 /// </summary>59 public void Start()60 {61 //启动任务调度框架62 if (!_quartzConfiguration.Scheduler.IsStarted)63 {64 _quartzConfiguration.Scheduler.Start();65 }66 }67 68 /// <summary>69 /// 等待任务完成并停止任务70 /// </summary>71 public void ShutDown()72 {73 if (_quartzConfiguration.Scheduler.IsStarted && !_quartzConfiguration.Scheduler.IsShutdown)74 {75 _quartzConfiguration.Scheduler.Shutdown();76 }77 }78 }
IQuartzConfiguration是对IScheduler的一个简单封装,创建默认的调度程序:
1 public class QuartzConfiguration : IQuartzConfiguration2 {3 public IScheduler Scheduler => StdSchedulerFactory.GetDefaultScheduler();4 }
接下来就是任务监听,Quartz提供了一个很好的扩展,我们只需实现IJobListener接口即可,包含如下多个方法体:
- JobExecutionVetoed
- JobToBeExecuted
- JobWasExecuted
查看字面意思,我们就能很好理解就是对每一个job在执行中的阶段所对应的事件,方法体内给出IJobExecutionContext当前执行的上下文,通过上下文我们可以输出许多我们想要的东西,例如当前执行Job的Name,执行时间,等等,也提供了JobExecutionException等异常信息,可以监控Job执行过程中发生的错误,很方便。
好了在我们的寄宿服务,与基础服务搭建完毕后,接下来就是要实现在业务系统中我们自身的逻辑结构了,核心程序类Servcie.cs,在service中我们需要将我们如上的配置进行加载,并同时扫描我们业务系统中的定时服务类,挨个进行启动,代码如下:
public class Service { /// <summary> /// 任务调度框架 /// </summary> private IQuartzScheduleJobManager QuartzScheduleJobManager { get { return IocManager.Instance.Resolve<IQuartzScheduleJobManager>(); } } /// <summary> /// 任务调度配置 /// </summary> public IQuartzConfiguration QuartzConfiguration { get { return IocManager.Instance.Resolve<IQuartzConfiguration>(); } } public Service() { try {
//注册我们的任务调度程序配置
IocManager.Register<IQuartzConfiguration, QuartzConfiguration>();
//注册任务监听程序到Ioc
IocManager.Register<IJobListener, QuartzJobListener>();
//声明一个接口可以被多个实例实现 IocManager.IocContainer.Kernel.Resolver.AddSubResolver(new ArrayResolver(TongTongMallBootstrapper.IocManager.IocContainer.Kernel, true)); } catch (Exception ex) { LogHelper.LogException(ex); } }
//设置我们的配置项 public void QuartzConfigurationInitialize() { //Job映射工厂 QuartzConfiguration.Scheduler.JobFactory = new QuartzJobFactory(IocManager.Instance); //Job 监听配置 QuartzConfiguration.Scheduler.ListenerManager.AddJobListener(IocManager.Instance.Resolve<IJobListener>()); } /// <summary> /// 基础类服务 /// </summary> public void Start() { //订阅各项自动服务 IocManager.Instance.IocContainer.Register(Classes.FromThisAssembly().BasedOn<IAutoService>().WithService.Base()); QuartzConfigurationInitialize(); RegisterService(); } /// <summary> /// 停止自动服务 /// </summary> public void Stop() { QuartzScheduleJobManager.ShutDown(); } /// <summary> /// 结束自动服务 /// </summary> public void Shutdown() { TongTongMallBootstrapper.Dispose(); QuartzScheduleJobManager.ShutDown(); } /// <summary> /// 注册服务 /// </summary> /// <param name="args"></param> public void RegisterService() { foreach (var service in IocManager.Instance.IocContainer.ResolveAll<IAutoService>()) { if (service.IsEnable) { LogHelper.Logger.Debug($"{service.ServiceName}服务正在启动中..."); service.Start(); } } LogHelper.Logger.Debug($"任务总调度工厂启动!"); QuartzScheduleJobManager.Start(); } }
在系统中我们主要使用了IOC进行了一个服务的自动扫描与切入,在服务配置完毕后,接下来我们只需定义业务相关的自动服务即可,通过继承我们的抽象类AutoServiceBase,接下来就很方便的打造每一个自动任务调度了,而关于Quartz的任务调度形式与时间配置,不是本文的重点介绍内容,就不在详说,不过在任务调度中我们也可以实现很多自定义的时间调度模式,例如自定义的节假日,或者每周一执行任务调度都可以,这个需要进行进一步编码实现。
由于公司方面,代码就不在给连接资源下载了,代码给出只是思路,当然在这里面还可以有更多的扩展,而本文只是方便快速上手为第一原则,文章有写的不当的地方,请及时指出,如本文对您有所帮助,也请点个推荐,您的肯定也是是我最大的动力。thanks
- 无需Get更多技能,快速打造一个可持久化的任务调度
- 无需Get更多技能,快速打造一个可持久化的任务调度
- 无需Get更多技能,快速打造一个可持久化的任务调度
- 使用spring quartz 进行持久化的任务调度
- Quartz-任务调度信息持久化到DB中
- BZOJ3932 CQOI2015 任务查询系统-可持久化线段树-可持久化平衡树
- [BZOJ3932] [CQOI2015]任务查询系统 && 可持久化线段树
- 【BZOJ3932】【CQOI2015】任务查询系统 可持久化线段树
- [CQOI2015]任务查询系统 (可持久化treap)
- 可持久化线段树 模板 【bzoj3932】任务查询系统
- BZOJ3932: [CQOI2015]任务查询系统 可持久化线段树
- bzoj3932:任务查询系统(可持久化线段树)
- 【CQOI2015】任务查询系统 可持久化Treap
- 基于Spring任务调度器实现可动态启停的任务调度器
- 一个简单的集群任务调度框架
- 一个任务调度问题
- (C++)定制一个可输出更多信息的assert
- 技能的可流动性
- Linux 配置LNMP服务器 并配置虚拟主机
- AOP 注解重复两次问题
- 枞阳新农合
- JavaScript进阶篇
- Java中堆内存和栈内存详解
- 无需Get更多技能,快速打造一个可持久化的任务调度
- 通用链接(Universal Links)实践笔记
- Android Studio Build Error : This version of Android Studio is incompatible with the Gradle Plugin u
- 3D-Warping
- MFC|Console Output from a GUI program(MFC调用cout输出)
- Python爬虫入门一之综述
- makefile知识点整理(持续不断更新)
- JSON
- 慎用Java递归调用