Quartz.NET是一个非常强大的作业调度框架,适用于各种定时执行的业务处理等,类似于WINDOWS自带的任务计划程序,其中运用Cron表达式来实现各种定时触发条件是我认为最为惊喜的地方。
Quartz.NET主要用到下面几个类:
IScheduler --调度器
IJobDetail --作业任务
ITrigger --触发器
如果我们自己采用Timer来写类似的定时执行任务程序的话,相应的我们应该有:(以下均为设想,目的是让大家搞清楚Quartz.NET上面三个接口的关系)
ScheduleTimer --Timer,每秒执行一次;
TriggerClass --判断是否需要执行作业任务,ScheduleTimer 每执行一次,就应该新开线程调用TriggerClass成员 NeedExecute方法或属性;
JobClass--具体的作业任务类,TriggerClass,若TriggerClass.NeedExecute返回true,那么就应该执行JobClass成员Execute方法;
好了,有关Quartz.NET的介绍非常之多,我这里不在多说,下面将主要介绍如何实现伪AOP写LOG功能。
AOP不知道,请点击此处了解。
Quartz.NET虽然已经集成了log4net的写日志功能,只需在Config配置好即可,但我觉得框架里面写的日志不符合我的要求,故我需要按照实际业务需要在某些条件才进行写LOG,故才有了这篇文章。
以下是实现了一个Job包裹类,也可以看作是Job的代理类,完整代码如下:
[DisallowConcurrentExecution] public class JobWraper<TJob> : IJob where TJob : IJob, new() { private static int syncFlag = 0; private IJob jobInner = null; public JobWraper() { jobInner = Activator.CreateInstance<TJob>(); } public void Execute(IJobExecutionContext context) { if (Interlocked.Increment(ref syncFlag) != 1) return; //忙判断 try { jobInner.Execute(context); } catch (Exception ex) { Master.WriteMsg(context.JobDetail.Key + "执行异常:" + ex.Message + Environment.NewLine + ex.StackTrace, true, true); } Interlocked.Exchange(ref syncFlag, 0); //解除忙 }
代码很简单,一般人都看得懂,我只是说重点:
1.syncFlag静态字段,目的是用来标记是否忙或者不忙,1代表不忙,其它代表忙,Interlocked.Increment与Interlocked.Exchange的用法是原子级的,确保每次只能有一个线程进行操作,类似于SQL中的独占锁,与lock有点相同,但又不同,如果用lock将整个执行都用大括号包起来,那么锁的范围比较广而且不易控制,而Interlocked只需要在需要的时候才独占,而且独占的时间非常短,其他大部份时间都是正常,而且更易可控,这就是我喜欢用他的原因。
2.为什么标记忙与不忙,原因是我必需确保每次执行的业务逻辑能够执行完成,而不要出现未执行完成,下一次的执行点又到了,造成多次甚至重复执行。
2.为什么要包裹,原因是我不想每个Job类里面都写try catch异常捕获及忙与不忙的判断,这样普通类只需专注业务处理即可。至于被包裹的类不一定非要IJob接口,可以自定义各类接口,但一定要有无参构造函数,否则就无法创建包裹的类的实例了。
通过上面的讲解,大家应该都明白了,下面是我为了便于集成管理Job,封装了一个JobManager任务管理类,完整代码如下:(代码比较简单,不再说明)
public class JobManager { private IScheduler scheduler = null; private int schedulerState = 0; public Dictionary<string, JobWithTrigger> JobTriggers { get; private set; } private IScheduler GetAScheduler() { var stdSchedulerFactory = new StdSchedulerFactory(); scheduler = stdSchedulerFactory.GetScheduler(); return scheduler; } public JobManager() { scheduler = GetAScheduler(); JobTriggers = new Dictionary<string, JobWithTrigger>(); } public JobWithTrigger CreateJobWithTrigger<TJob>(string cronExpr, IDictionary<string, object> jobData = null) where TJob : IJob { var jobType = typeof(TJob); string jobTypeName = jobType.Name; if (jobType.IsGenericType) { jobTypeName = jobType.GetGenericArguments()[0].Name; } IJobDetail job = null; if (jobData == null) job = JobBuilder.Create<TJob>().WithIdentity(jobTypeName).Build(); else job = JobBuilder.Create<TJob>().WithIdentity(jobTypeName).UsingJobData(new JobDataMap(jobData)).Build(); ITrigger trigger = TriggerBuilder.Create().WithIdentity(jobTypeName + "-Trigger").ForJob(job).StartNow().WithCronSchedule(cronExpr).Build(); var jt = new JobWithTrigger(job, trigger); JobTriggers[jt.Key] = jt; return jt; } public void ScheduleJobs(params JobWithTrigger[] jts) { if (scheduler.IsShutdown) { scheduler = GetAScheduler(); } foreach (var jt in jts) { scheduler.ScheduleJob(jt.JobDetail, jt.Trigger); } } public void ScheduleJobs(params string[] jtKeys) { var jts = JobTriggers.Where(t => jtKeys.Contains(t.Key)).Select(t => t.Value).ToArray(); ScheduleJobs(jts); } public void UnscheduleJobs(params TriggerKey[] triggerKeys) { scheduler.UnscheduleJobs(triggerKeys.ToList()); } public void UnscheduleJobs(params string[] jtKeys) { var triggerKeyObjs = JobTriggers.Where(t => jtKeys.Contains(t.Key)).Select(t => t.Value.Trigger.Key).ToArray(); UnscheduleJobs(triggerKeyObjs); } public int State { get { return schedulerState; //0:未开始,1:开始,2:暂停,3:恢复,-1:停止 } } [MethodImpl(MethodImplOptions.Synchronized)] public void Start() { if (schedulerState > 0) return; scheduler.Start(); schedulerState = 1; Master.WriteMsg("AutoTimingExecSystem程序已启动,所有任务按计划开始执行。", false, true); } [MethodImpl(MethodImplOptions.Synchronized)] public void Stop() { if (schedulerState <= 0) return; scheduler.Clear(); scheduler.Shutdown(); schedulerState = -1; Master.WriteMsg("AutoTimingExecSystem程序已停止,所有任务停止执行。", false, true); } [MethodImpl(MethodImplOptions.Synchronized)] public void Pause() { if (schedulerState != 1) return; scheduler.PauseAll(); schedulerState = 2; Master.WriteMsg("所有任务被取消或暂停执行。", false, true); } [MethodImpl(MethodImplOptions.Synchronized)] public void Resume() { if (schedulerState != 2) return; scheduler.ResumeAll(); schedulerState = 1; Master.WriteMsg("所有任务重新恢复执行。", false, true); } }
JobWithTrigger:任务与触发器关联类
[Serializable] public class JobWithTrigger { public JobWithTrigger() { this.Key = Guid.NewGuid().ToString("N"); } public JobWithTrigger(IJobDetail job, ITrigger trigger) : this() { this.JobDetail = job; this.Trigger = trigger; } public IJobDetail JobDetail { get; set; } public ITrigger Trigger { get; set; } public string JobName { get { return this.JobDetail.Key.Name; } } public string TriggerName { get { return this.Trigger.Key.Name; } } public string Key { get; private set; } }
用法比较简单,示例代码如下:
var jobManager = new JobManager(); var jt=jobManager.CreateJobWithTrigger<JobWraper<TestJob>>("0/5 * * * * ?"); //这里面可以将jt的保存或显示到任务界面上... jobManager.ScheduleJobs(JobWithTrigger的KEY数组 或 JobWithTrigger对象) jobManager.Start(); jobManager.Stop();
jobManager支持反复开启与关闭。