简介

  虽然Quartz.NET被园子里的大神们写烂了,自己还是整理了一篇,结尾会附上源码地址。

  Quartz.NET是一款功能齐全的开源作业调度框架,小至的应用程序,大到企业系统都可以适用。Quartz是作者James House用JAVA语言编写的,而Quartz.NET是从Quartz移植过来的C#版本。

  在一般企业,可以利用Quartz.Net框架做各种的定时任务,例如,数据迁移、跑报表等等。

  另外还有一款Hangfire https://www.hangfire.io/,也是作业调度框架,有自带监控web后台,比Quartz.Net更加易用,简单。但是Cron最低只支持到分钟级。然而Hangfire不是今天的主角,有机会再介绍。

简单例子

新建一个控制台项目,通过Nuget管理下载Quartz包

  1. using System;
  2. using System.Collections.Specialized;
  3. using Quartz;
  4. using Quartz.Impl;
  5.  
  6. namespace QuartzDotNetDemo
  7. {
  8. class Program
  9. {
  10. static void Main(string[] args)
  11. {
  12. //创建一个调度器工厂
  13. var props = new NameValueCollection
  14. {
  15. { "quartz.scheduler.instanceName", "QuartzDotNetDemo" }
  16. };
  17. var factory = new StdSchedulerFactory(props);
  18.  
  19. //获取调度器
  20. var sched = factory.GetScheduler();
  21. sched.Start();
  22.  
  23. //定义一个任务,关联"HelloJob"
  24. var job = JobBuilder.Create<HelloJob>()
  25. .WithIdentity("myJob", "group1")
  26. .Build();
  27.  
  28. //由触发器每40秒触发执行一次任务
  29. var trigger = TriggerBuilder.Create()
  30. .WithIdentity("myTrigger", "group1")
  31. .StartNow()
  32. .WithSimpleSchedule(x => x
  33. .WithIntervalInSeconds()
  34. .RepeatForever())
  35. .Build();
  36.  
  37. sched.ScheduleJob(job, trigger);
  38. }
  39. }
  40.  
  41. public class HelloJob : IJob
  42. {
  43. public void Execute(IJobExecutionContext context)
  44. {
  45. Console.WriteLine("你好");
  46. }
  47. }
  48. }

一个简单的调度任务流程如下:

概念

  有几个重要类和概念需要了解一下:

  • IScheduler - 与调度器交互的主要API.
  • IJob -由执行任务实现的接口。
  • IJobDetail - 定义Job实例
  • ITrigger - 按照定义的时间让任务执行的组件.
  • JobBuilder - 用于定义或者创建JobDetai
  • TriggerBuilder -用于定义或生成触发器实例

  他们之间的关系大概如下:

  当有空闲线程同时,到了该执行的时间,那么就会由Trigger去触发绑定的Job执行它的Excute方法,假如这次没执行完,却到了下一次的运行时间,如果有空闲线程就仍然会再次执行。但是如果没有空闲线程,会等到腾出空闲的线程才会执行,但是超过quartz.jobStore.misfireThreshold设置的时间就会放弃这次的运行。

  当然也可以在Job贴上DisallowConcurrentExecution标签让Job进行单线程跑,避免没跑完时的重复执行。

改造

  在第一个简单的demo里是无法良好的在实际中使用,因此我们需要改造一下。

需要的第三方包:

  • Autofac version="4.6.2"
  • Autofac.Extras.Quartz version="3.4.0"
  • Common.Logging version="3.4.1"
  • Common.Logging.Core version="3.4.1"
  • Common.Logging.Log4Net1213 version="3.4.1"
  • log4net version="2.0.3"
  • Newtonsoft.Json version="10.0.3"
  • Quartz version="2.6.1"
  • Topshelf version="4.0.3"
  • Topshelf.Autofac version="3.1.1"
  • Topshelf.Log4Net version="3.2.0"
  • Topshelf.Quartz version="0.4.0.1"

Topshelf

  Topshelf是一款为了方便安装部署在Windows系统下而诞生的宿主框架,它基于控制台项目,为开发人员带来更方便的调试和部署。

  官网:https://topshelf.readthedocs.io/en/latest/index.html

  那我们可以在Program.cs里写入以下代码:

  1. using Topshelf;
  2. using Topshelf.Autofac;
  3.  
  4. namespace QuartzDotNetDemo
  5. {
  6. class Program
  7. {
  8. static void Main(string[] args)
  9. {
  10. HostFactory.Run(config =>
  11. {
  12. config.SetServiceName(JobService.ServiceName);
  13. config.SetDescription("Quartz.NET的demo");
  14. config.UseLog4Net();
  15. config.UseAutofacContainer(JobService.Container);
  16.  
  17. config.Service<JobService>(setting =>
  18. {
  19. JobService.InitSchedule(setting);
  20. setting.ConstructUsingAutofacContainer();
  21. setting.WhenStarted(o => o.Start());
  22. setting.WhenStopped(o => o.Stop());
  23. });
  24. });
  25. }
  26. }
  27. }

JobService

  此类用来读取配置信息、初始化调度任务和注入ioc容器

  1. public class JobService
  2. {
  3. #region 初始化
  4. private static readonly ILog Log = LogManager.GetLogger(typeof(JobService));
  5.  
  6. private const string JobFile = "JobsConfig.xml";
  7. private static readonly string JobNamespceFormat;
  8. public static readonly string ServiceName;
  9. private static readonly Jobdetail[] JobList;
  10. public static IContainer Container;
  11.  
  12. static JobService()
  13. {
  14. var job = JobFile.XmlToObject<JobsConfig>();
  15.  
  16. ServiceName = job.Quartz.ServiceName;
  17. JobNamespceFormat = job.Quartz.Namespace;
  18. JobList = job.Quartz.JobList.JobDetail;
  19.  
  20. Log.Info("Jobs.xml 初始化完毕");
  21.  
  22. InitContainer();
  23. }
  24. #endregion
  25.  
  26. /// <summary>
  27. /// 初始化调度任务
  28. /// </summary>
  29. /// <param name="svc"></param>
  30. public static void InitSchedule(ServiceConfigurator<JobService> svc)
  31. {
  32. svc.UsingQuartzJobFactory(Container.Resolve<IJobFactory>);
  33.  
  34. foreach (var job in JobList)
  35. {
  36. svc.ScheduleQuartzJob(q =>
  37. {
  38. q.WithJob(JobBuilder.Create(Type.GetType(string.Format(JobNamespceFormat, job.JobName)))
  39. .WithIdentity(job.JobName, ServiceName)
  40. .Build);
  41.  
  42. q.AddTrigger(() => TriggerBuilder.Create()
  43. .WithCronSchedule(job.Cron)
  44. .Build());
  45.  
  46. Log.InfoFormat("任务 {0} 已完成调度设置", string.Format(JobNamespceFormat, job.JobName));
  47. });
  48. }
  49.  
  50. Log.Info("调度任务 初始化完毕");
  51. }
  52.  
  53. /// <summary>
  54. /// 初始化容器
  55. /// </summary>
  56. private static void InitContainer()
  57. {
  58. var builder = new ContainerBuilder();
  59. builder.RegisterModule(new QuartzAutofacFactoryModule());
  60. builder.RegisterModule(new QuartzAutofacJobsModule(typeof(JobService).Assembly));
  61. builder.RegisterType<JobService>().AsSelf();
  62.  
  63. var execDir = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
  64. var files = Directory.GetFiles(execDir, "QuartzDotNetDemo.*.dll", SearchOption.TopDirectoryOnly);
  65. if (files.Length > )
  66. {
  67. var assemblies = new Assembly[files.Length];
  68. for (var i = ; i < files.Length; i++)
  69. assemblies[i] = Assembly.LoadFile(files[i]);
  70.  
  71. builder.RegisterAssemblyTypes(assemblies)
  72. .Where(t => t.GetInterfaces().ToList().Contains(typeof(IService)))
  73. .AsSelf()
  74. .InstancePerLifetimeScope();
  75. }
  76.  
  77. Container = builder.Build();
  78. Log.Info("IOC容器 初始化完毕");
  79. }
  80.  
  81. public bool Start()
  82. {
  83. Log.Info("服务已启动");
  84. return true;
  85. }
  86.  
  87. public bool Stop()
  88. {
  89. Container.Dispose();
  90. Log.Info("服务已关闭");
  91. return false;
  92. }
  93. }

触发器类型

  一共有4种:

  • WithCalendarIntervalSchedule
  • WithCronSchedule
  • WithDailyTimeIntervalSchedule
  • WithSimpleSchedule

  在项目中使用的是WithCronSchedule,因为cron表达式更加灵活、方便。

Cron表达式

字段名 是否必填 值范围 特殊字符
Seconds YES 0-59 , - * /
Minutes YES 0-59 , - * /
Hours YES 0-23 , - * /
Day of month YES 1-31 , - * ? / L W
Month YES 1-12 or JAN-DEC , - * /
Day of week YES 1-7 or SUN-SAT , - * ? / L #
Year NO empty, 1970-2099 , - * /

例子:

  "0 0/5 * * * ?"    ---- 每5分钟触发一次

  "10 0/5 * * * ?"   -----每5分钟触发一次,每分钟10秒(例如:10:00:10 am,10:05:10,等等)

  "0 0/30 8-9 5,20 * ?" ----在每个月的第5到20个小时之间,每隔半小时就会触发一个触发点。请注意,触发器不会在上午10点触发,仅在8点,8点30分,9点和9点30分

BaseJob

  我们定义一个BaseJob写入公共处理逻辑,例如:业务逻辑禁用、公共异常日志消息推送等等。再由具体的Job去继承重写基类的ExecuteJob,简单的适配器模式运用。

  1. public abstract class BaseJob : IJob
  2. {
  3. protected readonly CommonService CommonService;
  4.  
  5. protected BaseJob(CommonService commonService)
  6. {
  7. CommonService = commonService;
  8. }
  9.  
  10. public void Execute(IJobExecutionContext context)
  11. {
  12. //公共逻辑
  13. CommonService.Enabled();
  14.  
  15. //job逻辑
  16. ExecuteJob(context);
  17. }
  18.  
  19. public abstract void ExecuteJob(IJobExecutionContext context);
  20. }

结束

  最后按照惯例双手奉上demo源码。https://github.com/SkyChenSky/QuartzDotNetDemo.git

  如果错误麻烦在下面评论指出,我会及时修改。

Quartz.NET的使用(附源码)的更多相关文章

  1. 【转】.NET(C#):浅谈程序集清单资源和RESX资源 关于单元测试的思考--Asp.Net Core单元测试最佳实践 封装自己的dapper lambda扩展-设计篇 编写自己的dapper lambda扩展-使用篇 正确理解CAP定理 Quartz.NET的使用(附源码) 整理自己的.net工具库 GC的前世与今生 Visual Studio Package 插件开发之自动生

    [转].NET(C#):浅谈程序集清单资源和RESX资源   目录 程序集清单资源 RESX资源文件 使用ResourceReader和ResourceSet解析二进制资源文件 使用ResourceM ...

  2. 使用sqlserver搭建高可用双机热备的Quartz集群部署【附源码】

    一般拿Timer和Quartz相比较的,简直就是对Quartz的侮辱,两者的功能根本就不在一个层级上,如本篇介绍的Quartz强大的序列化机制,可以序列到 sqlserver,mysql,当然还可以在 ...

  3. 在网站开发中很有用的8个 jQuery 效果【附源码】

    jQuery 作为最优秀 JavaScript 库之一,改变了很多人编写 JavaScript 的方式.它简化了 HTML 文档遍历,事件处理,动画和 Ajax 交互,而且有成千上万的成熟 jQuer ...

  4. Web 开发中很实用的10个效果【附源码下载】

    在工作中,我们可能会用到各种交互效果.而这些效果在平常翻看文章的时候碰到很多,但是一时半会又想不起来在哪,所以养成知识整理的习惯是很有必要的.这篇文章给大家推荐10个在 Web 开发中很有用的效果,记 ...

  5. MVC系列——MVC源码学习:打造自己的MVC框架(二:附源码)

    前言:上篇介绍了下 MVC5 的核心原理,整篇文章比较偏理论,所以相对比较枯燥.今天就来根据上篇的理论一步一步进行实践,通过自己写的一个简易MVC框架逐步理解,相信通过这一篇的实践,你会对MVC有一个 ...

  6. C#进阶系列——一步一步封装自己的HtmlHelper组件:BootstrapHelper(三:附源码)

    前言:之前的两篇封装了一些基础的表单组件,这篇继续来封装几个基于bootstrap的其他组件.和上篇不同的是,这篇的有几个组件需要某些js文件的支持. 本文原创地址:http://www.cnblog ...

  7. 轻量级通信引擎StriveEngine —— C/S通信demo(2) —— 使用二进制协议 (附源码)

    在网络上,交互的双方基于TCP或UDP进行通信,通信协议的格式通常分为两类:文本消息.二进制消息. 文本协议相对简单,通常使用一个特殊的标记符作为一个消息的结束. 二进制协议,通常是由消息头(Head ...

  8. jquery自定义插件结合baiduTemplate.js实现异步刷新(附源码)

    上一篇记录了BaiduTemplate模板引擎使用示例附源码,在此基础上对使用方法进行了封装 自定义插件jajaxrefresh.js 代码如下: //闭包限定命名空间 (function ($) { ...

  9. 精选9个值得学习的 HTML5 效果【附源码】

    这里精选了一组很酷的 HTML5 效果.HTML5 是现 Web 开发领域的热点, 拥有很多让人期待已久的新特性,特别是在移动端,Web 开发人员可以借助 HTML5 强大功能轻松制作各种交互性强.效 ...

  10. C#/ASP.NET MVC微信公众号接口开发之从零开发(四) 微信自定义菜单(附源码)

    C#/ASP.NET MVC微信接口开发文章目录: 1.C#/ASP.NET MVC微信公众号接口开发之从零开发(一) 接入微信公众平台 2.C#/ASP.NET MVC微信公众号接口开发之从零开发( ...

随机推荐

  1. 升级Linux tar &&解决某用tar解压失败的tar包

    今天解压个文件,出来很多这样的: /bin/tar: Ignoring unknown extended header keyword `SCHILY.dev'/bin/tar: Ignoring u ...

  2. mongodb 常用的命令

    mongodb 常用的命令 对数据库的操作,以及登录 1 进入数据库 use admin 2 增加或修改密码 db.addUser('wsc', '123') 3查看用户列表 db.system.us ...

  3. MySQL的一点浅显知识

    本人最近看了一本有关于MySQL的书籍<MySQL必知必会>,书中只写了一些基本知识,但是也基本涵盖了所有的MySQL的知识点.其余的比较高级的也只是在基础上进行扩展或者是优化,看完这本书 ...

  4. 优秀的CSS预处理----Less

    Less语法整理 本人邮箱:kk306484328@163.com,欢迎交流讨论. 欢迎转载,转载请注明网址:http://www.cnblogs.com/kk-here/p/7601058.html ...

  5. 记录各种IE兼容问题,IE6,IE7,IE8,IE9,IE10

     记录遇到的IE BUG:  1.IE8开发者工具打不开 解决办法:IE8新增了开发人员工具,非常不错,比早期的DevToolbar好用多了.不过在我的Win7下 使用的时候偶尔会出现一个莫名其妙的问 ...

  6. Linux 安装Anaconda 4.4.0

    安装步骤参考了官网的说明:https://docs.anaconda.com/anaconda/install/linux.html 具体步骤如下:  1.在官网下载地址 https://www.an ...

  7. 深入浅出AQS之条件队列

    相比于独占锁跟共享锁,AbstractQueuedSynchronizer中的条件队列可能被关注的并不是很多,但它在阻塞队列的实现里起着至关重要的作用,同时如果想全面了解AQS,条件队列也是必须要学习 ...

  8. Coin Change (IV) (dfs)

    Coin Change (IV) Time Limit: 1000MS   Memory Limit: 32768KB   64bit IO Format: %lld & %llu [Subm ...

  9. 关于离线底图和离线shp文件的加载

    首先底图是我自己用百度地图18级别的瓦片图在armap中制作的TPK文件,shp图层是我用同样的百度地图18级别的瓦片图矢量化的,二者在arcmap中的空间参考是一致的,所以我以为在移动端加入的时候二 ...

  10. RobotFramework自动化测试框架-移动手机自动化测试Click Element关键字的使用

    Click Element关键字用来模拟点击APP界面上的一个元素,该关键字接收一个参数[ locator ] ,这里的locator指的是界面元素的定位方式. 示例1:使用Click Element ...