Quartz.Net State Maintenance And Exception Handling

正如标题所示,文章主要是围绕Quartz.Net做业调度框架话题展开的,内容出自博主学习官方Examples的学习心得与体会,文中不免会有错误之处,还请指出得以指教。java

本篇关于Quart.Net涉及到的关键词是参数传递,状态维持,异常处理。框架

一:传参less

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using Quartz.Impl;
using Common.Logging;

namespace Quartz.Examples
{
    public class YZRExample3:IExample
    {
        #region IExample 成员

        public string Name
        {
            get { return GetType().Name; }
        }

        public void Run()
        {
            ILog log = LogManager.GetLogger(typeof(JobStateExample));

            log.Info("------- 初始化 -------------------");

            // First we must get a reference to a scheduler
            ISchedulerFactory sf = new StdSchedulerFactory();
            IScheduler sched = sf.GetScheduler();

            log.Info("------- 初始化完成 --------");

            log.Info("------- 开始调度队列进程 ----------------");

            DateTimeOffset startTime = DateBuilder.NextGivenSecondDate(null, 10);

            IJobDetail job1 = JobBuilder.Create<ColorJob>()
                .WithIdentity("job1", "group1")
                .Build();

            ISimpleTrigger trigger1 = (ISimpleTrigger)TriggerBuilder.Create()
                                                           .WithIdentity("trigger1", "group1")
                                                           .StartAt(startTime)
                                                           .WithSimpleSchedule(x => x.WithIntervalInSeconds(10).WithRepeatCount(4))
                                                           .Build();
   
            //将初始化参数传递到做业中
            job1.JobDataMap.Put(ColorJob.FavoriteColor, "Green");
            job1.JobDataMap.Put(ColorJob.ExecutionCount, 1);

            DateTimeOffset scheduleTime1 = sched.ScheduleJob(job1, trigger1);
            log.Info(string.Format("{0} will run at: {1} and repeat: {2} times, every {3} seconds", job1.Key, scheduleTime1.ToString("r"), trigger1.RepeatCount, trigger1.RepeatInterval.TotalSeconds));
                
            log.Info("------- Starting Scheduler ----------------");

            // All of the jobs have been added to the scheduler, but none of the jobs
            // will run until the scheduler has been started
            sched.Start();

            log.Info("------- Started Scheduler -----------------");

            log.Info("------- Waiting 60 seconds... -------------");
            try
            {
                // wait five minutes to show jobs
                Thread.Sleep(300 * 1000);
                // executing...
            }
            catch (ThreadInterruptedException)
            {
            }

            log.Info("------- Shutting Down ---------------------");

            sched.Shutdown(true);

            log.Info("------- Shutdown Complete -----------------");

            SchedulerMetaData metaData = sched.GetMetaData();
            log.Info(string.Format("Executed {0} jobs.", metaData.NumberOfJobsExecuted));
        }

        #endregion
    }
}
YZRExample3
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;


namespace Quartz.Examples
{
    using Common.Logging;
    /// <summary>
    /// This is just a simple job that receives parameters and maintains state.
    /// 演示一个简单的传接参以及状态保持===>主程序能够经过jobDataMap初始化参数,job类经过JobDataMap来获取参数,而且状态保持。
    /// </summary>
    /// <author>Bill Kratzer</author>
    /// <author>Marko Lahma (.NET)</author>
    [PersistJobDataAfterExecution]
    [DisallowConcurrentExecution]
    public class ColorJob : IJob
    {
        private static readonly ILog log = LogManager.GetLogger(typeof(ColorJob));

        // parameter names specific to this job
        public const string FavoriteColor = "favorite color";
        public const string ExecutionCount = "count";

        // Since Quartz will re-instantiate a class every time it
        // gets executed, members non-static member variables can
        // not be used to maintain state!
        private int counter = 1;

        /// <summary>
        /// Called by the <see cref="IScheduler" /> when a
        /// <see cref="ITrigger" /> fires that is associated with
        /// the <see cref="IJob" />.
        /// </summary>
        public virtual void Execute(IJobExecutionContext context)
        {

            // This job simply prints out its job name and the
            // date and time that it is running
            JobKey jobKey = context.JobDetail.Key;

            // Grab and print passed parameters
            JobDataMap data = context.JobDetail.JobDataMap;
            string favoriteColor = data.GetString(FavoriteColor);
            int count = data.GetInt(ExecutionCount);
            log.InfoFormat(
                "ColorJob: {0} executing at {1}\n  favorite color is {2}\n  execution count (from job map) is {3}\n  execution count (from job member variable) is {4}",
                jobKey,
                DateTime.Now.ToString("r"),
                favoriteColor,
                count, counter);

            // increment the count and store it back into the job map so that job state can be properly maintained
            //增长计数并将其储存在job map中,以使job state获得适当的维护
            count++;
            data.Put(ExecutionCount, count);

            // Increment the local member variable 
            //增量本地成员变量
            // This serves no real purpose since job state can not be maintained via member variables!
            // 这并非真正的目的,由于job state不能经过成员变量来保持!
            counter++;
        }

    }
}
ColorJob
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Quartz.Examples
{
    /// <summary>
    /// Interface for examples.
    /// </summary>
    /// <author>Marko Lahma (.NET)</author>
    public interface IExample
    {
        string Name
        {
            get;
        }

        void Run();
    }
}
IExample

对于以上代码还有不熟悉的,建议请先看前面几章的介绍,这有助于你理解接下来讨论的话题。ide

上述代码咱们构建了一个简单的轮询调度做业,惟一只有两行代码比较陌生新鲜:学习

//将初始化参数传递到做业中
job1.JobDataMap.Put(ColorJob.FavoriteColor, "Green");
job1.JobDataMap.Put(ColorJob.ExecutionCount, 1);

IJob接口下定义了public virtual void Execute(IJobExecutionContext context);方法,注意到这个方法定义了一个IJobExecutionContext类型的参数,能够经过前面的几个例子知道这个context能够获得:ui

JobKey jobKey = context.JobDetail.Key;//做业的名称
String description=context.JobDetail.Description;//做业的描述
bool durable=context.JobDetail.Durable;
this

这里还能够获得另一个,没错没就是JobDataMap(Key-Value)spa

JobDataMap data = context.JobDetail.JobDataMap;3d

咱们能够经过JobDataMap来传送参数给做业类,而且JobDataMap里的参数键值对会获得状态保持。并且,上面例子也说明了在做业类定义成员变量将不会获得状态维持。code

PS:Map在java中是一个接口类型,有HashMap,TreeMap,它们自己都是键值对的形式保存数据。

除了job1.JobDataMap.Put()这种显示的方式传送参数以外,还可使用UsingJobData()来传递参数给做业类:

在定义做业类的时候使用UsingJobData()传递参数:

 

IJobDetail job = JobBuilder.Create<ColorJob>()
                .WithIdentity("statefulJob1", "group1")
                .UsingJobData(StatefulDumbJob.ExecutionDelay, 10000L)
                .Build();

 

 

在ColorJob中JobDataMap data = context.JobDetail.JobDataMap;  data会包含使用UsingJobData()传递的参数:

 

二:做业与做业(一样的做业)之间参数以及状态是否会互相干扰?

添加多一个做业ColorJob,而且也传送参数,代码以下:

 

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using Quartz.Impl;
using Common.Logging;

namespace Quartz.Examples
{
    public class YZRExample3:IExample
    {
        #region IExample 成员

        public string Name
        {
            get { return GetType().Name; }
        }

        public void Run()
        {
            ILog log = LogManager.GetLogger(typeof(JobStateExample));

            log.Info("------- 初始化 -------------------");

            // First we must get a reference to a scheduler
            ISchedulerFactory sf = new StdSchedulerFactory();
            IScheduler sched = sf.GetScheduler();

            log.Info("------- 初始化完成 --------");

            log.Info("------- 开始调度队列进程 ----------------");

            // get a "nice round" time a few seconds in the future....
            DateTimeOffset startTime = DateBuilder.NextGivenSecondDate(null, 10);

            // job1 will only run 5 times (at start time, plus 4 repeats), every 10 seconds
            IJobDetail job1 = JobBuilder.Create<ColorJob>()
                .WithIdentity("job1", "group1")
                .Build();

            ISimpleTrigger trigger1 = (ISimpleTrigger)TriggerBuilder.Create()
                                                           .WithIdentity("trigger1", "group1")
                                                           .StartAt(startTime)
                                                           .WithSimpleSchedule(x => x.WithIntervalInSeconds(10).WithRepeatCount(4))
                                                           .Build();

            // pass initialization parameters into the job
            //将初始化参数传递到做业中
            job1.JobDataMap.Put(ColorJob.FavoriteColor, "Green");
            job1.JobDataMap.Put(ColorJob.ExecutionCount, 1);

            // schedule the job to run
            DateTimeOffset scheduleTime1 = sched.ScheduleJob(job1, trigger1);
            log.Info(string.Format("{0} will run at: {1} and repeat: {2} times, every {3} seconds", job1.Key, scheduleTime1.ToString("r"), trigger1.RepeatCount, trigger1.RepeatInterval.TotalSeconds));


            //做业与做业之间的状态是分开的,不会互相干扰
            // job2 will also run 5 times, every 10 seconds

            IJobDetail job2 = JobBuilder.Create<ColorJob>()
                .WithIdentity("job2", "group1")
                .Build();

            ISimpleTrigger trigger2 = (ISimpleTrigger)TriggerBuilder.Create()
                                                           .WithIdentity("trigger2", "group1")
                                                           .StartAt(startTime)
                                                           .WithSimpleSchedule(x => x.WithIntervalInSeconds(10).WithRepeatCount(4))
                                                           .Build();

            // pass initialization parameters into the job
            // this job has a different favorite color!
            job2.JobDataMap.Put(ColorJob.FavoriteColor, "Red");
            job2.JobDataMap.Put(ColorJob.ExecutionCount, 1);

            // schedule the job to run
            DateTimeOffset scheduleTime2 = sched.ScheduleJob(job2, trigger2);
            log.Info(string.Format("{0} will run at: {1} and repeat: {2} times, every {3} seconds", job2.Key, scheduleTime2.ToString("r"), trigger2.RepeatCount, trigger2.RepeatInterval.TotalSeconds));
            
            
            log.Info("------- Starting Scheduler ----------------");

            // All of the jobs have been added to the scheduler, but none of the jobs
            // will run until the scheduler has been started
            sched.Start();

            log.Info("------- Started Scheduler -----------------");

            log.Info("------- Waiting 60 seconds... -------------");
            try
            {
                // wait five minutes to show jobs
                Thread.Sleep(300 * 1000);
                // executing...
            }
            catch (ThreadInterruptedException)
            {
            }

            log.Info("------- Shutting Down ---------------------");

            sched.Shutdown(true);

            log.Info("------- Shutdown Complete -----------------");

            SchedulerMetaData metaData = sched.GetMetaData();
            log.Info(string.Format("Executed {0} jobs.", metaData.NumberOfJobsExecuted));
        }

        #endregion
    }
}
YZRExample3

 

当你运行代码看了打印信息以后,就能够得知,做业与做业之间是相互区分的,不会互相干扰。

 

 

三:延迟执行

   存在一种可能就是在轮询的时间周期很短,但咱们的做业执行须要花费的时间较长,这样的状况下咱们须要让下一次轮询的时机到来之时先等待做业执行完毕,再触发下一次时机。
   这样子会致使的结果是:假如做业执行花费的时间为10s,而程序定制的轮询周期为每5s一次,这样等价于下一次轮询到的时机到来时上一次做业还没
来得及完成,因此下一次触发必须先等待做业完成以后再触发。很明显结果就是轮询周期再也不是指定的每5s一次,而是10s一次。

//延迟10秒再继续重复做业
            IJobDetail job = JobBuilder.Create<StatefulDumbJob>()
                .WithIdentity("statefulJob1", "group1")
                .UsingJobData(StatefulDumbJob.ExecutionDelay, 10000L)
                .Build();

            ISimpleTrigger trigger = (ISimpleTrigger)TriggerBuilder.Create()
                                                          .WithIdentity("trigger1", "group1")
                                                          .StartAt(startTime)
                                                          .WithSimpleSchedule(x => x.WithIntervalInSeconds(5).RepeatForever())
                                                          .Build();

            DateTimeOffset ft = sched.ScheduleJob(job, trigger);
            log.Info(string.Format("{0} will run at: {1} and repeat: {2} times, every {3} seconds", job.Key, ft.ToString("r"), trigger.RepeatCount, trigger.RepeatInterval.TotalSeconds));
            */


            //比较合适的写法是:当没有在轮询时机触发时使用WithMisfireHandlingInstructionNowWithExistingCount()
            // statefulJob2 will run every three seconds
            // (but it will delay for ten seconds - and therefore purposely misfire after a few iterations)
            IJobDetail job = JobBuilder.Create<StatefulDumbJob>()
                .WithIdentity("statefulJob2", "group1")
                .UsingJobData(StatefulDumbJob.ExecutionDelay, 10000L)
                .Build();

            ISimpleTrigger trigger = (ISimpleTrigger)TriggerBuilder.Create()
                                           .WithIdentity("trigger2", "group1")
                                           .StartAt(startTime)
                                           .WithSimpleSchedule(x => x
                                                                 .WithIntervalInSeconds(3)
                                                                 .RepeatForever()
                                                                 .WithMisfireHandlingInstructionNowWithExistingCount()) // set misfire instructions
                                           .Build();
            DateTimeOffset ft = sched.ScheduleJob(job, trigger);

            log.Info(string.Format("{0} will run at: {1} and repeat: {2} times, every {3} seconds", job.Key, ft.ToString("r"), trigger.RepeatCount, trigger.RepeatInterval.TotalSeconds));
View Code

 

四:异常JobExecutionException

1.发生异常时程序会抛出异常信息,而且恢复继续调度

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Common.Logging;

namespace Quartz.Examples
{
    /// <summary>
    /// A job dumb job that will throw a job execution exception.
    /// 异常处理:
    /// 在做业发生异常的时候,抛出一个异常,而且程序对异常进行处理以后,RefireImmediately会立刻恢复做业的继续调度
    /// </summary>
    /// <author>Bill Kratzer</author>
    /// <author>Marko Lahma (.NET)</author>
    [PersistJobDataAfterExecution]
    [DisallowConcurrentExecution]
    public class BadJob1 : IJob
    {
        // Logging
        private static readonly ILog log = LogManager.GetLogger(typeof(BadJob1));

        /// <summary>
        /// Called by the <see cref="IScheduler" /> when a Trigger" />
        /// fires that is associated with the <see cref="IJob" />.
        /// </summary>
        public virtual void Execute(IJobExecutionContext context)
        {
            JobKey jobKey = context.JobDetail.Key;
            JobDataMap dataMap = context.JobDetail.JobDataMap;

            int denominator = dataMap.GetInt("denominator");
            log.InfoFormat("{0} with denominator {1}", "---" + jobKey + " executing at " + DateTime.Now, denominator);


            // a contrived example of an exception that
            // will be generated by this job due to a 
            // divide by zero error (only on first run)
            try
            {
                int calculation = 4815 / denominator;
            }
            catch (Exception e)
            {
                log.Info("--- Error in job!");
                JobExecutionException e2 = new JobExecutionException(e);


                // fix denominator so the next time this job run
                // it won't fail again
                dataMap.Put("denominator", "1");

                // this job will refire immediately
                e2.RefireImmediately = true;
                throw e2;
            }

            log.InfoFormat("---{0} completed at {1}", jobKey, DateTime.Now.ToString("r"));
        }

    }
}
BadJob1
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Common.Logging;
using Quartz.Impl;
using System.Threading;

namespace Quartz.Examples
{
    public class YZRExample5:IExample
    {
        #region IExample 成员

        public string Name
        {
            get { return GetType().Name; }
        }

        public void Run()
        {
            ILog log = LogManager.GetLogger(typeof(JobExceptionExample));

            log.Info("------- Initializing ----------------------");

            // First we must get a reference to a scheduler
            ISchedulerFactory sf = new StdSchedulerFactory();
            IScheduler sched = sf.GetScheduler();

            log.Info("------- Initialization Complete ------------");

            log.Info("------- Scheduling Jobs -------------------");

            DateTimeOffset startTime = DateBuilder.NextGivenSecondDate(null, 15);

            
            //模拟做业内部发生异常,但进过异常处理以后,做业会继续进行调度
            IJobDetail job = JobBuilder.Create<BadJob1>()
                .WithIdentity("badJob1", "group1")
                .UsingJobData("denominator", "0")//使用除数为0模拟异常
                .Build();

            ISimpleTrigger trigger = (ISimpleTrigger)TriggerBuilder.Create()
                                                          .WithIdentity("trigger1", "group1")
                                                          .StartAt(startTime)
                                                          .WithSimpleSchedule(x => x.WithIntervalInSeconds(10).RepeatForever())
                                                          .Build();

            DateTimeOffset ft = sched.ScheduleJob(job, trigger);
            log.Info(job.Key + " will run at: " + ft + " and repeat: "
                     + trigger.RepeatCount + " times, every "
                     + trigger.RepeatInterval.TotalSeconds + " seconds");
         
            log.Info("------- Starting Scheduler ----------------");

           
            sched.Start();

            log.Info("------- Started Scheduler -----------------");

            // sleep for 30 seconds
            try
            {
                Thread.Sleep(TimeSpan.FromSeconds(30));
            }
            catch (ThreadInterruptedException)
            {
            }

            log.Info("------- Shutting Down ---------------------");

            sched.Shutdown(false);

            log.Info("------- Shutdown Complete -----------------");

            SchedulerMetaData metaData = sched.GetMetaData();
            log.Info(string.Format("Executed {0} jobs.", metaData.NumberOfJobsExecuted));
        }

        #endregion
    }
}
YZRExample5

2.发生异常时会抛出异常信息,而且中止调度

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Common.Logging;

namespace Quartz.Examples
{
    /// <summary>
    /// A job dumb job that will throw a job execution exception.
    /// 抛出异常,而且与当前做业有关的全部trigger都将会被中止调度
    /// </summary>
    /// <author>Bill Kratzer</author>
    /// <author>Marko Lahma (.NET)</author>
    [PersistJobDataAfterExecution]
    [DisallowConcurrentExecution]
    public class BadJob2 : IJob
    {
        // Logging
        private static readonly ILog log = LogManager.GetLogger(typeof(BadJob2));

        /// <summary>
        /// Called by the <see cref="IScheduler" /> when a <see cref="ITrigger" />
        /// fires that is associated with the <see cref="IJob" />.
        /// <para>
        /// The implementation may wish to set a  result object on the
        /// JobExecutionContext before this method exits.  The result itself
        /// is meaningless to Quartz, but may be informative to
        /// <see cref="IJobListener" />s or
        /// <see cref="ITriggerListener" />s that are watching the job's
        /// execution.
        /// </para>
        /// </summary>
        /// <param name="context">Execution context.</param>
        public virtual void Execute(IJobExecutionContext context)
        {
            JobKey jobKey = context.JobDetail.Key;
            log.InfoFormat("---{0} executing at {1}", jobKey, System.DateTime.Now.ToString("r"));

            // a contrived example of an exception that will be generated by this job due to a divide by zero error
            // 在一个做业里人为的抛出一个异常
            // 
            try
            {
                int zero = 0;
                int calculation = 4815 / zero;
            }
            catch (System.Exception e)
            {
                log.Info("--- Error in job!");
                JobExecutionException e2 = new JobExecutionException(e);
                // Quartz will automatically unschedule
                // all triggers associated with this job
                // so that it does not run again
                e2.UnscheduleAllTriggers = true;
                throw e2;
            }

            log.InfoFormat("---{0} completed at {1}", jobKey, System.DateTime.Now.ToString("r"));
        }
    }
}
BadJob2
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Common.Logging;
using Quartz.Impl;
using System.Threading;

namespace Quartz.Examples
{
    public class YZRExample5:IExample
    {
        #region IExample 成员

        public string Name
        {
            get { return GetType().Name; }
        }

        public void Run()
        {
            ILog log = LogManager.GetLogger(typeof(JobExceptionExample));

            log.Info("------- Initializing ----------------------");

            // First we must get a reference to a scheduler
            ISchedulerFactory sf = new StdSchedulerFactory();
            IScheduler sched = sf.GetScheduler();

            log.Info("------- Initialization Complete ------------");

            log.Info("------- Scheduling Jobs -------------------");

           
            DateTimeOffset startTime = DateBuilder.NextGivenSecondDate(null, 15);
             //引发异常的做业将会中止全部相关的trigger的运行而且抛出异常
            job = JobBuilder.Create<BadJob2>()
                .WithIdentity("badJob2", "group1")
                .Build();

            trigger = (ISimpleTrigger)TriggerBuilder.Create()
                                           .WithIdentity("trigger2", "group1")
                                           .StartAt(startTime)
                                           .WithSimpleSchedule(x => x.WithIntervalInSeconds(5).RepeatForever())
                                           .Build();
            ft = sched.ScheduleJob(job, trigger);
            log.Info(string.Format("{0} will run at: {1} and repeat: {2} times, every {3} seconds", job.Key, ft.ToString("r"), trigger.RepeatCount, trigger.RepeatInterval.TotalSeconds));
            
            log.Info("------- Starting Scheduler ----------------");

            sched.Start();

            log.Info("------- Started Scheduler -----------------");

            // sleep for 30 seconds
            try
            {
                Thread.Sleep(TimeSpan.FromSeconds(30));
            }
            catch (ThreadInterruptedException)
            {
            }

            log.Info("------- Shutting Down ---------------------");

            sched.Shutdown(false);

            log.Info("------- Shutdown Complete -----------------");

            SchedulerMetaData metaData = sched.GetMetaData();
            log.Info(string.Format("Executed {0} jobs.", metaData.NumberOfJobsExecuted));
        }

        #endregion
    }
}
YZRExample5
相关文章
相关标签/搜索