项目总是很忙,忙里偷闲还是要总结一下,前一段时间,由于项目中需要,我们需要很多定时汇总数据的情况,项目初期主要使用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了,代码如下:

  1. class Program
  2. {
  3. static void Main(string[] args)
  4. {
  5. HostFactory.Run(x =>
  6. {
  7. x.Service<Service>(s =>
  8. {
  9. s.ConstructUsing(name => new Service());
  10. s.WhenStarted(tc => tc.Start());
  11. s.WhenStopped(tc => tc.Stop());
  12. s.WhenShutdown(tc => tc.Shutdown());
  13. });
  14. x.RunAsLocalSystem();
  15. x.SetDescription("服务集合,包含自动化消息,响应队列的领域事件");
  16. x.SetDisplayName("Test.Services");
  17. x.SetServiceName("Test.Services");
  18. });
  19. }
  20. }

topshelf是一个很简单就能创建service的工具,它开源在GitHub TopShelf上,是一款快速搭建mono与windows service ,在编写好我们的服务代码,只需简单地启动代码,就能创建一个持久化的服务,不过想要兼容在Mono上也是非常简单,只需加入如下一句代码即可。

  1. 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 : IQuartzScheduleJobManager
  24. {
  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 : IJob
  42. {
  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();
  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 : IQuartzConfiguration
  2. {
  3. public IScheduler Scheduler => StdSchedulerFactory.GetDefaultScheduler();
  4. }

接下来就是任务监听,Quartz提供了一个很好的扩展,我们只需实现IJobListener接口即可,包含如下多个方法体:

  1. JobExecutionVetoed
  2. JobToBeExecuted
  3. JobWasExecuted

查看字面意思,我们就能很好理解就是对每一个job在执行中的阶段所对应的事件,方法体内给出IJobExecutionContext当前执行的上下文,通过上下文我们可以输出许多我们想要的东西,例如当前执行Job的Name,执行时间,等等,也提供了JobExecutionException等异常信息,可以监控Job执行过程中发生的错误,很方便。

好了在我们的寄宿服务,与基础服务搭建完毕后,接下来就是要实现在业务系统中我们自身的逻辑结构了,核心程序类Servcie.cs,在service中我们需要将我们如上的配置进行加载,并同时扫描我们业务系统中的定时服务类,挨个进行启动,代码如下:

  1. public class Service
  2. {
  3. /// <summary>
  4. /// 任务调度框架
  5. /// </summary>
  6. private IQuartzScheduleJobManager QuartzScheduleJobManager
  7. {
  8. get
  9. {
  10. return IocManager.Instance.Resolve<IQuartzScheduleJobManager>();
  11. }
  12. }
  13.  
  14. /// <summary>
  15. /// 任务调度配置
  16. /// </summary>
  17. public IQuartzConfiguration QuartzConfiguration
  18. {
  19. get
  20. {
  21. return IocManager.Instance.Resolve<IQuartzConfiguration>();
  22. }
  23. }
  24.  
  25. public Service()
  26. {
  27. try
  28. {
    //注册我们的任务调度程序配置
    IocManager.Register<IQuartzConfiguration, QuartzConfiguration>();
    //注册任务监听程序到Ioc
     IocManager.Register<IJobListener, QuartzJobListener>();
    //声明一个接口可以被多个实例实现
  29. IocManager.IocContainer.Kernel.Resolver.AddSubResolver(new ArrayResolver(TongTongMallBootstrapper.IocManager.IocContainer.Kernel, true));
  30. }
  31. catch (Exception ex)
  32. {
  33. LogHelper.LogException(ex);
  34. }
  35. }

  36. //设置我们的配置项
  37. public void QuartzConfigurationInitialize()
  38. {
  39. //Job映射工厂
  40. QuartzConfiguration.Scheduler.JobFactory = new QuartzJobFactory(IocManager.Instance);
  41. //Job 监听配置
  42. QuartzConfiguration.Scheduler.ListenerManager.AddJobListener(IocManager.Instance.Resolve<IJobListener>());
  43. }
  44.  
  45. /// <summary>
  46. /// 基础类服务
  47. /// </summary>
  48. public void Start()
  49. {
  50. //订阅各项自动服务
  51. IocManager.Instance.IocContainer.Register(Classes.FromThisAssembly().BasedOn<IAutoService>().WithService.Base());
  52.  
  53. QuartzConfigurationInitialize();
  54.  
  55. RegisterService();
  56. }
  57.  
  58. /// <summary>
  59. /// 停止自动服务
  60. /// </summary>
  61. public void Stop()
  62. {
  63. QuartzScheduleJobManager.ShutDown();
  64. }
  65.  
  66. /// <summary>
  67. /// 结束自动服务
  68. /// </summary>
  69. public void Shutdown()
  70. {
  71. TongTongMallBootstrapper.Dispose();
  72. QuartzScheduleJobManager.ShutDown();
  73. }
  74.  
  75. /// <summary>
  76. /// 注册服务
  77. /// </summary>
  78. /// <param name="args"></param>
  79. public void RegisterService()
  80. {
  81. foreach (var service in IocManager.Instance.IocContainer.ResolveAll<IAutoService>())
  82. {
  83. if (service.IsEnable)
  84. {
  85. LogHelper.Logger.Debug($"{service.ServiceName}服务正在启动中...");
  86. service.Start();
  87. }
  88. }
  89. LogHelper.Logger.Debug($"任务总调度工厂启动!");
  90. QuartzScheduleJobManager.Start();
  91. }
  92. }

在系统中我们主要使用了IOC进行了一个服务的自动扫描与切入,在服务配置完毕后,接下来我们只需定义业务相关的自动服务即可,通过继承我们的抽象类AutoServiceBase,接下来就很方便的打造每一个自动任务调度了,而关于Quartz的任务调度形式与时间配置,不是本文的重点介绍内容,就不在详说,不过在任务调度中我们也可以实现很多自定义的时间调度模式,例如自定义的节假日,或者每周一执行任务调度都可以,这个需要进行进一步编码实现。

由于公司方面,代码就不在给连接资源下载了,代码给出只是思路,当然在这里面还可以有更多的扩展,而本文只是方便快速上手为第一原则,文章有写的不当的地方,请及时指出,如本文对您有所帮助,也请点个推荐,您的肯定也是是我最大的动力。thanks

    

无需Get更多技能,快速打造一个可持久化的任务调度的更多相关文章

  1. 使用go的ssh包快速打造一个本地命令行ssh客户端

    热身运动

  2. 2019基于Hexo快速搭建个人博客,打造一个炫酷博客(1)-奥怪的小栈

    本文转载于:奥怪的小栈 这篇文章告诉你如何在2019快速上手搭建一个像我一样的博客:基于HEXO+Github搭建.并完成SEO优化,打造一个炫酷博客. 本站基于HEXO+Github搭建.所以你需要 ...

  3. 打造一个高逼格的android开源项目——小白全攻略 (转)

    转自:打造一个高逼格的android开源项目 小引子 在平时的开发过程中,我们经常会查阅很多的资料,最常参考的是 github 的开源项目.通常在项目的主页面能看到项目的简介和基本使用,并且时不时能看 ...

  4. 基于AgileEAS.NET SOA 中间件领域模型数据器快速打造自己的代码生成器

    一.前言 AgileEAS.NET SOA 中间件平台是一款基于基于敏捷并行开发思想和Microsoft .Net构件(组件)开发技术而构建的一个快速开发应用平台.用于帮助中小型软件企业建立一条适合市 ...

  5. phantomjs + python 打造一个微信机器人

    phantomjs + python 打造一个微信机器人 1.前奏   媳妇公司不能上网,但经常需要在公众号上找一些文章做一些参考,需要的时候就把文章链接分享给我,然后我在浏览器打开网页,一点点复制过 ...

  6. 手把手带你打造一个 Android 热修复框架(上篇)

    本文来自网易云社区 作者:王晨彦 前言 热修复和插件化是目前 Android 领域很火热的两门技术,也是 Android 开发工程师必备的技能. 目前比较流行的热修复方案有微信的 Tinker,手淘的 ...

  7. 使用 Sealos 在 3 分钟内快速部署一个生产级别的 Kubernetes 高可用集群

    本文首发于:微信公众号「运维之美」,公众号 ID:Hi-Linux. 「运维之美」是一个有情怀.有态度,专注于 Linux 运维相关技术文章分享的公众号.公众号致力于为广大运维工作者分享各类技术文章和 ...

  8. 微人事 star 数超 10k,如何打造一个 star 数超 10k 的开源项目

    看了下,微人事(https://github.com/lenve/vhr)项目 star 数超 10k 啦,松哥第一个 star 数过万的开源项目就这样诞生了. 两年前差不多就是现在这个时候,松哥所在 ...

  9. 收藏收藏:时隔一年,你关注的打造一个实用的TXT文本操作及日志框架,我们开源了,不再为程序写日志发愁(也支持.net core哦)

    记得做这个框架是在2018年刚接触.net core的时候,那个时候为了能够专心的研究我开始不写博客了,但是学有所成并在公司运用了近一年的时间了,决定回来和各位分享我们所掌握的那星星点点的知识,希望可 ...

随机推荐

  1. CentOS根分区占满

    我中奖了!!! 查看硬盘的使用情况(bjchenxu)df -k 以K为单位显示df -h 以人性化单位显示,可以是b,k,m,g,t.. 查看各目录占用大小 du -sh * du -ms /*

  2. 高仿xx教育网

    2014年2月26日 16:24:50 好久没做 php了,考虑到老婆是教育行业,高仿一个教育辅导机构的网站 加油

  3. 使用nodejs爬取和讯网高管增减持数据

    为了抓取和讯网高管增减持的数据,首先得分析一下数据的来源: 网址: http://stockdata.stock.hexun.com/ggzjc/history.shtml 使用chrome开发者工具 ...

  4. Object.create() 实现

    if (typeof Object.create !== 'function') { Object.create = function (o) { function F() {} F.prototyp ...

  5. 【转】sql语句的优化分析

    开门见山,问题所在 sql语句性能达不到你的要求,执行效率让你忍无可忍,一般会时下面几种情况. 网速不给力,不稳定. 服务器内存不够,或者SQL 被分配的内存不够. sql语句设计不合理 没有相应的索 ...

  6. vim列编辑

    命令模式下:ctrl + v(我在gvim,win7中是ctrl +shift + q)进入列编辑模,选中要编辑的行(j 上,k下) 输入 “I” (大写的 I,光标定位到选中的第一行),输入要编辑的 ...

  7. iOS 之 WebView 简单使用

    1. 代理 UIWebViewDelegate 2. 创建 UIWebView myWebView=[[UIWebView alloc] initWithFrame:CGRectMake(, ,sel ...

  8. iOS 错误 之 http请求

     Application Transport Security has blocked a cleartext HTTP (http://) resource load since it is ins ...

  9. tomcat 修改编码(Java之负基础实战)

    1.找到server.xml 在tomcat安装路径/conf/server.xml 2.设置POST和GET使用相同编码 useBodyEncodingForURI="true" ...

  10. Java-io流入门到精通详细总结

    IO流:★★★★★,用于处理设备上数据. 流:可以理解数据的流动,就是一个数据流.IO流最终要以对象来体现,对象都存在IO包中. 流也进行分类: 1:输入流(读)和输出流(写). 2:因为处理的数据不 ...