qurtz.net
Quartz.NET的使用(附源码)(作者 陈珙)
简介
虽然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包
- using System;
- using System.Collections.Specialized;
- using Quartz;
- using Quartz.Impl;
- namespace QuartzDotNetDemo
- {
- class Program
- {
- static void Main(string[] args)
- {
- //创建一个调度器工厂
- var props = new NameValueCollection
- {
- { "quartz.scheduler.instanceName", "QuartzDotNetDemo" }
- };
- var factory = new StdSchedulerFactory(props);
- //获取调度器
- var sched = factory.GetScheduler();
- sched.Start();
- //定义一个任务,关联"HelloJob"
- var job = JobBuilder.Create<HelloJob>()
- .WithIdentity("myJob", "group1")
- .Build();
- //由触发器每40秒触发执行一次任务
- var trigger = TriggerBuilder.Create()
- .WithIdentity("myTrigger", "group1")
- .StartNow()
- .WithSimpleSchedule(x => x
- .WithIntervalInSeconds()
- .RepeatForever())
- .Build();
- sched.ScheduleJob(job, trigger);
- }
- }
- public class HelloJob : IJob
- {
- public void Execute(IJobExecutionContext context)
- {
- Console.WriteLine("你好");
- }
- }
- }
一个简单的调度任务流程如下:
概念
有几个重要类和概念需要了解一下:
- 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里写入以下代码:
- using Topshelf;
- using Topshelf.Autofac;
- namespace QuartzDotNetDemo
- {
- class Program
- {
- static void Main(string[] args)
- {
- HostFactory.Run(config =>
- {
- config.SetServiceName(JobService.ServiceName);
- config.SetDescription("Quartz.NET的demo");
- config.UseLog4Net();
- config.UseAutofacContainer(JobService.Container);
- config.Service<JobService>(setting =>
- {
- JobService.InitSchedule(setting);
- setting.ConstructUsingAutofacContainer();
- setting.WhenStarted(o => o.Start());
- setting.WhenStopped(o => o.Stop());
- });
- });
- }
- }
- }
JobService
此类用来读取配置信息、初始化调度任务和注入ioc容器
- public class JobService
- {
- #region 初始化
- private static readonly ILog Log = LogManager.GetLogger(typeof(JobService));
- private const string JobFile = "JobsConfig.xml";
- private static readonly string JobNamespceFormat;
- public static readonly string ServiceName;
- private static readonly Jobdetail[] JobList;
- public static IContainer Container;
- static JobService()
- {
- var job = JobFile.XmlToObject<JobsConfig>();
- ServiceName = job.Quartz.ServiceName;
- JobNamespceFormat = job.Quartz.Namespace;
- JobList = job.Quartz.JobList.JobDetail;
- Log.Info("Jobs.xml 初始化完毕");
- InitContainer();
- }
- #endregion
- /// <summary>
- /// 初始化调度任务
- /// </summary>
- /// <param name="svc"></param>
- public static void InitSchedule(ServiceConfigurator<JobService> svc)
- {
- svc.UsingQuartzJobFactory(Container.Resolve<IJobFactory>);
- foreach (var job in JobList)
- {
- svc.ScheduleQuartzJob(q =>
- {
- q.WithJob(JobBuilder.Create(Type.GetType(string.Format(JobNamespceFormat, job.JobName)))
- .WithIdentity(job.JobName, ServiceName)
- .Build);
- q.AddTrigger(() => TriggerBuilder.Create()
- .WithCronSchedule(job.Cron)
- .Build());
- Log.InfoFormat("任务 {0} 已完成调度设置", string.Format(JobNamespceFormat, job.JobName));
- });
- }
- Log.Info("调度任务 初始化完毕");
- }
- /// <summary>
- /// 初始化容器
- /// </summary>
- private static void InitContainer()
- {
- var builder = new ContainerBuilder();
- builder.RegisterModule(new QuartzAutofacFactoryModule());
- builder.RegisterModule(new QuartzAutofacJobsModule(typeof(JobService).Assembly));
- builder.RegisterType<JobService>().AsSelf();
- var execDir = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
- var files = Directory.GetFiles(execDir, "QuartzDotNetDemo.*.dll", SearchOption.TopDirectoryOnly);
- if (files.Length > )
- {
- var assemblies = new Assembly[files.Length];
- for (var i = ; i < files.Length; i++)
- assemblies[i] = Assembly.LoadFile(files[i]);
- builder.RegisterAssemblyTypes(assemblies)
- .Where(t => t.GetInterfaces().ToList().Contains(typeof(IService)))
- .AsSelf()
- .InstancePerLifetimeScope();
- }
- Container = builder.Build();
- Log.Info("IOC容器 初始化完毕");
- }
- public bool Start()
- {
- Log.Info("服务已启动");
- return true;
- }
- public bool Stop()
- {
- Container.Dispose();
- Log.Info("服务已关闭");
- return false;
- }
- }
触发器类型
一共有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,简单的适配器模式运用。
- public abstract class BaseJob : IJob
- {
- protected readonly CommonService CommonService;
- protected BaseJob(CommonService commonService)
- {
- CommonService = commonService;
- }
- public void Execute(IJobExecutionContext context)
- {
- //公共逻辑
- CommonService.Enabled();
- //job逻辑
- ExecuteJob(context);
- }
- public abstract void ExecuteJob(IJobExecutionContext context);
- }
结束
最后按照惯例双手奉上demo源码。https://github.com/SkyChenSky/QuartzDotNetDemo.git
如果错误麻烦在下面评论指出,我会及时修改。
作 者: 陈珙
出 处:http://www.cnblogs.com/skychen1218/
关于作者:专注于微软平台的项目开发。如有问题或建议,请多多赐教!
版权声明:本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文链接。
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是作者坚持原创和持续写作的最大动力
qurtz.net的更多相关文章
- qurtz.net(转载)
Quartz+TopShelf实现Windows服务作业调度 Quartz:首先我贴出来了两段代码(下方),可以看出,首先会根据配置文件(quartz.config),包装出一个Quartz.Co ...
- 使用Timer和ScheduledThreadPoolExecutor执行定时任务
Java使用Timer和ScheduledThreadPoolExecutor执行定时任务 定时任务是在指定时间执行程序,或周期性执行计划任务.Java中实现定时任务的方法有很多,主要JDK自带的一些 ...
- java并发面试
1.在java中守护线程和本地线程区别? java中的线程分为两种:守护线程(Daemon)和用户线程(User). 任何线程都可以设置为守护线程和用户线程,通过方法Thread.setDaemon( ...
- Java并发编程75道面试题及答案
1.在java中守护线程和本地线程区别? java中的线程分为两种:守护线程(Daemon)和用户线程(User). 任何线程都可以设置为守护线程和用户线程,通过方法Thread.setDaemon( ...
- Java并发编程75个问答
1.在java中守护线程和本地线程区别? java中的线程分为两种:守护线程(Daemon)和用户线程(User). 任何线程都可以设置为守护线程和用户线程,通过方法Thread.setDaemon( ...
- Java并发编程73道面试题及答案
原文出处:https://blog.csdn.net/qq_34039315/article/details/7854931 1.在java中守护线程和本地线程区别? java中的线程分为两种:守护线 ...
- Java并发编程73道面试题及答案 —— 面试稳了
今天主要整理一下 Java 并发编程在面试中的常见问题,希望对需要的读者有用. 1.在java中守护线程和本地线程区别? java中的线程分为两种:守护线程(Daemon)和用户线程(User). 任 ...
- Java——并发编程
1.在java中守护线程和本地线程区别? java中的线程分为两种:守护线程(Daemon)和用户线程(User). 任何线程都可以设置为守护线程和用户线程,通过方法Thread.setDaemon( ...
- Java并发编程总结
基础概念 1.什么是原子操作?在Java Concurrency API中有哪些原子类(atomic classes)?原子操作(atomic operation)意为"不可被中断的一个或一 ...
随机推荐
- T-SQL 有参数存储过程的创建与执行
use StudentManager go if exists(select * from sysobjects where name='usp_ScoreQuery2') drop procedur ...
- Android Gradle插件(plugin)版本(version)与Gradle、SDK Build Tools版本关系
具体关系如下图: 比如,Android Studio 2.0发布,其中有个新功能“Instant Run”,需要Android Gradle Plugin版本2.0.0以上,那么我们项目的.gradl ...
- MySQL8.0.12版本密码修改策略问题
查看密码策略(修改临时密码之后才可查看) show variables like 'validate_password%'; 8之前 validate_password_ 8之后validat ...
- MySQL建立索引,触发器
创建索引: ALTER TABLE <表名> ADD INDEX (<字段>); >ALTER TABLE `table_name` ADD PRIMARY KEY (` ...
- 更优雅的使用Git
JavaScript之禅已经发过两篇 Git 相关的文章了.一篇文章,教你学会Git :浅显易懂,如果你还不会 Git 可以先去看看.Git的奇技淫巧 :介绍了一些实用的操作.今天为大家带来第三篇,如 ...
- JQuery字符串的操作
一.String对象属性 1.length属性: length算是字符串中非常常用的一个属性了,它的功能是获取字符串的长度.当然需要注意的是js中的中文每个汉字也只代表一个字符,这里可能跟其他语言有些 ...
- liinux安装 mysql 及主从复制
mariadb其实就是mysqlmysql已经被oracle收购,它即将闭源,马上要开始收费了因此还想免费试用开源的数据库mysql,就在centos7上,将mysql分支为mariadb 安装mar ...
- python连接数据库——create_engine和conn.cursor
python操作数据库的方法: 一种是导入sqlalchemy包,另一种是导入psycopg2包. 具体用法如下(此处以postgre数据库举例) 第一种: # 导入包 from sqlalchemy ...
- python-多线程和线程池
import threading # 点击查看它的用法 传统多线程方案会使用“即时创建, 即时销毁”的策略. from multiprocessing.dummy import Pool # 线程池 ...
- bootstrap-datepicker实现日期input readonly 标签中选择时间功能
引用datepicker css,js,zh-CH文件 ps: 都是基于bootstrap,所以得先引入bootstrap文件才可以使用 <link href="https://cdn ...