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

一: Calendar

前面演示过根据秒-分-时 日-月-星期等触发时机来定义一个轮询的作业调度,在实际生活中,除此之外还有根据日历来调度作业,根据日历定义触发轮询周期也是比较常用常见的功能。

在Quartz.Net中日历是由AnnualCalendar类定义的,实例化一个AnnualCalendar对象,往这个对象添加自定义的日期构成自定义的一个日历触发时机集合。

举个例子:

 

           //日历对象
            AnnualCalendar holidays = new AnnualCalendar();

            // 元旦
            DateTime NewYearDay = new DateTime(DateTime.UtcNow.Year, 1, 1);
            holidays.SetDayExcluded(NewYearDay , true);

            // 国庆节
            DateTime NationalDay= new DateTime(DateTime.UtcNow.Year, 10, 1);
            holidays.SetDayExcluded(NationalDay, true);

            // 光棍节
            DateTime SinglesDay= new DateTime(DateTime.UtcNow.Year, 11, 11);
            holidays.SetDayExcluded(SinglesDay, true);

 

有了日历对象之后,需要将日历对象附加到调度实例上,并且在触发器中使用ModifiedByCalendar("日历对象")来指定按照日历对象进行调度。

下面贴出根据日历对象指定的日期进行作业调度的代码(仅供参考):

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

namespace Quartz.Examples
{
    /// <summary> 
    /// This example will demonstrate how calendars can be used 
    /// to exclude periods of time when scheduling should not
    /// take place.
    /// 一个根据日历(节假日)来设定调度作业的演示
    /// </summary>
    /// <author>Marko Lahma (.NET)</author>
    public class CalendarExample : IExample
    {
        public string Name
        {
            get { return GetType().Name; }
        }

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

            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 -------------------");

            // Add the holiday calendar to the schedule   //就是自定义节假日   比如我们可以事先根据日历设定7月4号,10月31号,12月25号作为调度时机
            AnnualCalendar holidays = new AnnualCalendar();

            // fourth of July (July 4)
            DateTime fourthOfJuly = new DateTime(DateTime.UtcNow.Year, 7, 4);
            holidays.SetDayExcluded(fourthOfJuly, true);

            // halloween (Oct 31)
            DateTime halloween = new DateTime(DateTime.UtcNow.Year, 10, 31);//10月31号
            holidays.SetDayExcluded(halloween, true);

            // christmas (Dec 25)
            DateTime christmas = new DateTime(DateTime.UtcNow.Year, 12, 25);
            holidays.SetDayExcluded(christmas, true);

            // tell the schedule about our holiday calendar
            sched.AddCalendar("holidays", holidays, false, false);

           
            //设定开启调度日历的时间
            DateTimeOffset runDate = DateBuilder.DateOf(10, 0, 0, 31, 10);//10.31早上10点开启调度作业

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

            ISimpleTrigger trigger = (ISimpleTrigger)TriggerBuilder.Create()
                                                          .WithIdentity("trigger1", "group1")
                                                          .StartAt(runDate)
                                                          .WithSimpleSchedule(x => x.WithIntervalInHours(1).RepeatForever())
                                                          .ModifiedByCalendar("holidays")
                                                          .Build();

            // schedule the job and print the first run date
            DateTimeOffset firstRunTime = sched.ScheduleJob(job, trigger);

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

            log.Info("------- Starting Scheduler ----------------");
            sched.Start();
            log.Info("------- Waiting 30 seconds... --------------");
            try
            {
                // wait 30 seconds to show jobs
                Thread.Sleep(30 * 1000);
                // executing...
            }
            catch (ThreadInterruptedException)
            {
            }

            // shut down the scheduler
            log.Info("------- Shutting Down ---------------------");
            sched.Shutdown(true);
            log.Info("------- Shutdown Complete -----------------");

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

二:监听对象

在Quartz.Net框架中提供了一个监听器IJobListener接口,实现该接口实例化一个子类,这个子类可以监听一个作业id来触发本身的void JobWasExecuted(IJobExecutionContext inContext, JobExecutionException inException)方法:这个方法里面带了IJobExecutionContext inContext参数,inContext.Scheduler其实就是程序的调度实例,我们知道通过调度实例可以添加作业以及触发器(定制一个轮询的调度任务)并且Start()开启执行任务。

这样看来,通过实现IJobListener接口得到监听器类中的JobWasExecuted()方法里可以再次定义轮询调度作业。

比如当需要满足以下需求时可以使用监听器来实现:

=>在A任务顺利开启执行后,轮询调度B任务。(此时B任务就定义在监听器类里面)

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

namespace Quartz.Examples
{
    
    public class ListenerExample : IExample
    {
        public string Name
        {
            get { return GetType().Name; }
        }

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

            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 -------------------");

            // schedule a job to run immediately
            IJobDetail job = JobBuilder.Create<SimpleJob1>()
                .WithIdentity("job1")
                .Build();

            ITrigger trigger = TriggerBuilder.Create()
                .WithIdentity("trigger1")
                .StartNow()
                .Build();
            //IJobListener监听器接口,实现该接口得到自定义的Job1Listener类,在该类中实现JobWasExecuted()方法,在方法中可以添加作业触发器

            //监听类的意义更在于在一个作业成功运行之后,触发绑定的另外一些操作,这些操作在监听类中定义并调度。
            // Set up the listener
            //设定监听程序,实例话Job1Listener()监听类
            IJobListener listener = new Job1Listener();
            IMatcher<JobKey> matcher = KeyMatcher<JobKey>.KeyEquals(job.Key);
            sched.ListenerManager.AddJobListener(listener, matcher);//根据作业key为响应作业添加监听

         
            sched.ScheduleJob(job, trigger);
            log.Info("------- Starting Scheduler ----------------");
            sched.Start();
            log.Info("------- Waiting 30 seconds... --------------");
            try
            {
                // wait 30 seconds to show jobs
                Thread.Sleep(TimeSpan.FromSeconds(30));
                // executing...
            }
            catch (ThreadInterruptedException)
            {
            }


            // shut down the scheduler
            log.Info("------- Shutting Down ---------------------");
            sched.Shutdown(true);
            log.Info("------- Shutdown Complete -----------------");

            SchedulerMetaData metaData = sched.GetMetaData();
            log.Info(string.Format("Executed {0} jobs.", metaData.NumberOfJobsExecuted));
        }
    }
}
ListenerExample
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Common.Logging;

namespace Quartz.Examples
{
    ///监听程序可以附加到作业中,附加了监听器的作业在进行调度的时候,准备执行,否决,执行完毕三个状态
    ///并且在监听程序可以实例化IJobDetail类创建新作业
    public class Job1Listener : IJobListener
    {
        private static readonly ILog log = LogManager.GetLogger(typeof(Job1Listener));

        public virtual string Name
        {
            get { return "job1_to_job2"; }
        }

        public virtual void JobToBeExecuted(IJobExecutionContext inContext)
        {
            log.Info("Job1Listener says: Job Is about to be 执行.");//执行
        }

        public virtual void JobExecutionVetoed(IJobExecutionContext inContext)
        {
            log.Info("Job1Listener says: Job Execution was 否决.");//否决
        }

        public virtual void JobWasExecuted(IJobExecutionContext inContext, JobExecutionException inException)
        {
            log.Info("Job1Listener says: Job was 执行 完毕.");

            // Simple job #2
            //监听程序调度作业
            IJobDetail job2 = JobBuilder.Create<SimpleJob2>()
                .WithIdentity("job2")
                .Build();

            ITrigger trigger = TriggerBuilder.Create()
                .WithIdentity("job2Trigger")
                .StartNow()
                .Build();

            try
            {
                // schedule the job to run!
                inContext.Scheduler.ScheduleJob(job2, trigger);
            }
            catch (SchedulerException e)
            {
                log.Warn("Unable to schedule job2!");
                Console.Error.WriteLine(e.StackTrace);
            }
        }
    }
}
View Code

三:插件配置

官方给出的名字叫做插件,其实我认为,这只是一种关于如何调度作业的配置。

有三种配置方式:

1.通过代码实例化NameValueCollection对象,往NameValueCollection对象以键值对形式赋值,然后在调度工厂对象中传入该NameValueCollection对象得到调度实例。

            var properties = new NameValueCollection();
            properties["quartz.plugin.triggHistory.type"] = "Quartz.Plugin.History.LoggingJobHistoryPlugin";

            properties["quartz.plugin.jobInitializer.type"] = "Quartz.Plugin.Xml.XMLSchedulingDataProcessorPlugin";//插件
            properties["quartz.plugin.jobInitializer.fileNames"] = "quartz_jobs.xml";//读取这个配置文件
            properties["quartz.plugin.jobInitializer.failOnFileNotFound"] = "true";
            properties["quartz.plugin.jobInitializer.scanInterval"] = "120";//120秒一次

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

通过这样方式无须再使用代码定义作业对象IJobDetail以及触发器ITrigger等,而是通过properties["quartz.plugin.jobInitializer.fileNames"]指定的xml文件来设置:

<?xml version="1.0" encoding="UTF-8"?>

<job-scheduling-data xmlns="http://quartznet.sourceforge.net/JobSchedulingData"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                 version="2.0">

  <processing-directives>
    <overwrite-existing-data>true</overwrite-existing-data>
  </processing-directives>

  <schedule>
    
    <job>
      <name>jobName1</name>
      <group>jobGroup1</group>
      <description>jobDesciption1</description>
      <job-type>Quartz.Examples.SimpleJob3, Quartz.Examples</job-type>
      <durable>true</durable>
      <recover>false</recover>
      <job-data-map>
        <entry>
          <key>key0</key>
          <value>value0</value>
        </entry>
        <entry>
          <key>key1</key>
          <value>value1</value>
        </entry>
        <entry>
          <key>key2</key>
          <value>value2</value>
        </entry>
      </job-data-map>
    </job>
    
    <trigger>
      <simple>
        <name>simpleName</name>
        <group>simpleGroup</group>
        <description>SimpleTriggerDescription</description>
        <job-name>jobName1</job-name>
        <job-group>jobGroup1</job-group>
        <start-time>1982-06-28T18:15:00.0Z</start-time>
        <end-time>2020-05-04T18:13:51.0Z</end-time>
        <misfire-instruction>SmartPolicy</misfire-instruction>
        <repeat-count>100</repeat-count>
        <repeat-interval>3000</repeat-interval>
      </simple>
    </trigger>

  </schedule>
  
</job-scheduling-data>
quartz_jobs.xml

 

注意点:

 1.1 quartz_jobs.xml必须设置为始终复制=>右键属性,复制到输出目录选项选择始终复制

 1.2 要将NameValueCollection对象传入StdSchedulerFactory工厂中以得到调试实例对象

       ISchedulerFactory sf = new StdSchedulerFactory(properties);

 

2.通过app.config或者web.config配置文件

通过这种方式定义调度作业的信息将会放置在app.config或web.config配置文件中,在代码中只需要得到一个无参的StdSchedulerFactory()实例对象,开启调度即可:

 

            ISchedulerFactory sf = new StdSchedulerFactory();
            IScheduler sched = sf.GetScheduler();

            // start the schedule 
            sched.Start();

 

app.config配置文件内容:

<?xml version="1.0" encoding="utf-8"?>
<configuration>
    <configSections>
        <section name="quartz" type="System.Configuration.NameValueSectionHandler, System, Version=1.0.5000.0,Culture=neutral, PublicKeyToken=b77a5c561934e089" />
            <section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler, log4net" />
        <sectionGroup name="common">
            <section name="logging" type="Common.Logging.ConfigurationSectionHandler, Common.Logging" />
        </sectionGroup>
    </configSections>

    <common>
        <logging>
            <factoryAdapter type="Common.Logging.Log4Net.Log4NetLoggerFactoryAdapter, Common.Logging.Log4net1213">
                <arg key="configType" value="INLINE" />
            </factoryAdapter>
        </logging>
    </common>

    <log4net>
        <appender name="ConsoleAppender" type="log4net.Appender.ConsoleAppender">
            <layout type="log4net.Layout.PatternLayout">
                <conversionPattern value="%d [%t] %-5p %l - %m%n" />
            </layout>
        </appender>
        <appender name="EventLogAppender" type="log4net.Appender.EventLogAppender">
            <layout type="log4net.Layout.PatternLayout">
                <conversionPattern value="%d [%t] %-5p %l - %m%n" />
            </layout>
        </appender>
        <root>
            <level value="INFO" />
            <appender-ref ref="ConsoleAppender" />
      <!-- uncomment to enable event log appending -->
            <!-- <appender-ref ref="EventLogAppender" /> -->
        </root>
    </log4net>

  <!-- 
    We use quartz.config for this server, you can always use configuration section if you want to.
    Configuration section has precedence here.  
  -->
   
  <quartz>
    <add key="quartz.plugin.triggHistory.type" value="Quartz.Plugin.History.LoggingJobHistoryPlugin"/>
    <add key="quartz.plugin.jobInitializer.type" value="Quartz.Plugin.Xml.XMLSchedulingDataProcessorPlugin"/>
    <add key="quartz.plugin.jobInitializer.fileNames" value="quartz_jobs.xml"/>
    <add key="quartz.plugin.jobInitializer.failOnFileNotFound" value="true"/>
    <add key="quartz.plugin.jobInitializer.scanInterval" value="120"/>
  </quartz>
   
  <runtime>
    <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
      <dependentAssembly>
        <assemblyIdentity name="Common.Logging" publicKeyToken="af08829b84f0328e" culture="neutral" />
        <bindingRedirect oldVersion="0.0.0.0-3.0.0.0" newVersion="3.0.0.0" />
      </dependentAssembly>
      <dependentAssembly>
        <assemblyIdentity name="log4net" publicKeyToken="669e0ddf0bb1aa2a" culture="neutral" />
        <bindingRedirect oldVersion="0.0.0.0-1.2.13.0" newVersion="1.2.13.0" />
      </dependentAssembly>
    </assemblyBinding>
  </runtime>
</configuration>
App.config

在app.config中会指定调度任务信息的一个路径,比如quartz_jobs.xml文件,通过读取这个xml文件来获取调度任务。

 2.1 quartz_jobs.xml必须设置为始终复制=>右键属性,复制到输出目录选项选择始终复制

 

 

3.通过quartz.config配置文件

 

# You can configure your scheduler in either <quartz> configuration section
# or in quartz properties file
# Configuration section has precedence

quartz.scheduler.instanceName = ServerScheduler

# configure thread pool info
quartz.threadPool.type = Quartz.Simpl.SimpleThreadPool, Quartz
quartz.threadPool.threadCount = 10
quartz.threadPool.threadPriority = Normal

#--------------------------------*************plugin配置------------------------------------
# job initialization plugin handles our xml reading, without it defaults are used
quartz.plugin.xml.type = Quartz.Plugin.Xml.XMLSchedulingDataProcessorPlugin, Quartz
quartz.plugin.xml.fileNames = ~/quartz_jobs.xml

# export this server to remoting context
quartz.scheduler.exporter.type = Quartz.Simpl.RemotingSchedulerExporter, Quartz
quartz.scheduler.exporter.port = 555
quartz.scheduler.exporter.bindName = QuartzScheduler
quartz.scheduler.exporter.channelType = tcp
quartz.scheduler.exporter.channelName = httpQuartz
quartz.config

 

在代码中只需要得到一个无参的StdSchedulerFactory()实例对象,开启调度即可:

 

 

            ISchedulerFactory sf = new StdSchedulerFactory();
            IScheduler sched = sf.GetScheduler();

            // start the schedule 
            sched.Start();

 

 

 

 3.1 quartz_jobs.xml必须设置为始终复制=>右键属性,复制到输出目录选项选择始终复制

 3.2 quartz.config必须设置为始终复制=>右键属性,复制到输出目录选项选择始终复制

 

posted on 2016-04-23 14:53  巴夫巴夫  阅读(350)  评论(0编辑  收藏  举报