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的更多相关文章

  1. qurtz.net(转载)

    Quartz+TopShelf实现Windows服务作业调度   Quartz:首先我贴出来了两段代码(下方),可以看出,首先会根据配置文件(quartz.config),包装出一个Quartz.Co ...

  2. 使用Timer和ScheduledThreadPoolExecutor执行定时任务

    Java使用Timer和ScheduledThreadPoolExecutor执行定时任务 定时任务是在指定时间执行程序,或周期性执行计划任务.Java中实现定时任务的方法有很多,主要JDK自带的一些 ...

  3. java并发面试

    1.在java中守护线程和本地线程区别? java中的线程分为两种:守护线程(Daemon)和用户线程(User). 任何线程都可以设置为守护线程和用户线程,通过方法Thread.setDaemon( ...

  4. Java并发编程75道面试题及答案

    1.在java中守护线程和本地线程区别? java中的线程分为两种:守护线程(Daemon)和用户线程(User). 任何线程都可以设置为守护线程和用户线程,通过方法Thread.setDaemon( ...

  5. Java并发编程75个问答

    1.在java中守护线程和本地线程区别? java中的线程分为两种:守护线程(Daemon)和用户线程(User). 任何线程都可以设置为守护线程和用户线程,通过方法Thread.setDaemon( ...

  6. Java并发编程73道面试题及答案

    原文出处:https://blog.csdn.net/qq_34039315/article/details/7854931 1.在java中守护线程和本地线程区别? java中的线程分为两种:守护线 ...

  7. Java并发编程73道面试题及答案 —— 面试稳了

    今天主要整理一下 Java 并发编程在面试中的常见问题,希望对需要的读者有用. 1.在java中守护线程和本地线程区别? java中的线程分为两种:守护线程(Daemon)和用户线程(User). 任 ...

  8. Java——并发编程

    1.在java中守护线程和本地线程区别? java中的线程分为两种:守护线程(Daemon)和用户线程(User). 任何线程都可以设置为守护线程和用户线程,通过方法Thread.setDaemon( ...

  9. Java并发编程总结

    基础概念 1.什么是原子操作?在Java Concurrency API中有哪些原子类(atomic classes)?原子操作(atomic operation)意为"不可被中断的一个或一 ...

随机推荐

  1. sqlserver 模糊查询,连表,聚合函数,分组

    use StudentManageDB go select StudentName,StudentAddress from Students where StudentAddress like '天津 ...

  2. locals()和globals()

    都是获取当前作用域的内容: locals() 获取局部作用域的所有内容 函数内:获取locals()之前的,当前作用阈所有内容 函数外:获取打印前, 当前的作用域所有内容 在闭包内: 会把使用到的外层 ...

  3. Keras.applications.models权重:存储路径及加载

    网络中断原因导致keras加载vgg16等模型权重失败, 直接解决方法是:删掉下载文件,再重新下载 Windows-weights路径: C:\Users\你的用户名\.keras\models Li ...

  4. 使用LVS实现负载均衡原理及安装配置详解(转)

    负载均衡集群是 load balance 集群的简写,翻译成中文就是负载均衡集群.常用的负载均衡开源软件有nginx.lvs.haproxy,商业的硬件负载均衡设备F5.Netscale.这里主要是学 ...

  5. for循环.html

    <script> for (var i = 1; i < 5 ; i++) { alert(i); } 公式需要记住,并多加练习,加深记忆,注意编写格式 for (var i = 1 ...

  6. django模板(template)

    模板层(template) 你可能已经注意到我们在例子视图中返回文本的方式有点特别. 也就是说,HTML被直接硬编码在 Python代码之中. 1 2 3 4 def current_datetime ...

  7. day24类的继承

    类的继承1 什么是继承    继承一种新建类的方式,新建的类称之为子类/派生类,被继承的类称之为父类\基类\超类    python中继承的特点:        1. 子类可以遗传/重用父类的属性   ...

  8. angularjs探秘<四> 双向数据绑定

    双向数据绑定是angularjs的一大特性,这个特性在实际开发中省了不少事儿.之前第二篇提过数据绑定,这一篇从实际开发的案例中具体看下双向数据绑定的便捷. 首先看一个场景: 在 注册/登录 中经常遇到 ...

  9. 【Git使用】SourceTree+Git简单使用(Windows)(转)

    导读: 本人过去Git的可视化工具用的是TortoiseGit,虽然Android Studio也能进行版本管理,但是用下来,感觉SoureTree这款工具是最舒服的(免费的),下面就给大家介绍下我的 ...

  10. spring事务传播实现源码分析

    转载. https://blog.csdn.net/qpfjalzm123/article/details/83717367 本文只是对spring事务传播实现的流程进行简单的分析,如有不对之处请指出 ...