Quartz.net 在应用程序中统一调度各任务,Config文件配置所有的任务项

         偶然发现项目中有多个需要定时完成的任务,通过以下方式进行的。

  • 有一些是写在MSSql Server的作业中,这些是纯粹滴操作数据库的行为。
  • 有一些是写在Window Service中,用Timer定时器来轮询完成。既包括操作数据库,又包括发邮件,或者同步数据去其他系统。
         但是这些都1对1的,在SqlServer中,一个作业完成一件事件。有多个作业那就必须建立多个作业。需要移植到其他数据库就必须把每个作业的Sql复制过去执行,很繁琐。
         在Window Service中,也是1个任务对于1个服务,若有多个任务就建立多个服务。若要移植到其他机器,就得一个一个去安装这些服务。
         最重要的是,你都不清楚到底有多少Window Service是这个项目的,有多少作业在MSSql Server中运行。管理相当不方便。

         在可移植性方面,确实非常不方便。也不便于统一管理。头疼。

          最近研究Quartz.net,发现它能很方便地统一调度多个任务。若是Web的项目,只需在Application_Start方法中加载这些任务,并开始调度。在Application_Stop方法中将调度Shutdown。若有更改任务,只需重启应用程序即可。
          我进行了试验,将所有的任务放在配置文件中JobScheduler.config:
<?xml version="1.0" encoding="utf-8" ?>
<JobScheduler>
  <Job Description="作业1">
    <DllName>JobLibrary.dll</DllName>
    <JobDetail job="test1" group="test1Group" jobtype="JobLibrary.FirstJob" />
    <Trigger name="test1" group="test1Group" type="CronTrigger" expression="0/2 * * * * ?" />
  </Job>
  <Job Description="作业2">
    <DllName>JobLibrary.dll</DllName>
    <JobDetail job="test2" group="test2Group" jobtype="JobLibrary.SecondJob" />
    <Trigger name="test2" group="test2Group" type="CronTrigger" expression="0/5 * * * * ?" />
  </Job>
  <Job Description="作业3">
    <DllName>JobLibrary.dll</DllName>
    <JobDetail job="test3" group="test3Group" jobtype="JobLibrary.ThirdJob" />
    <Trigger name="test3" group="test3Group" type="CronTrigger" expression="0/3 * * * * ?" />
  </Job>
</JobScheduler>

                 统一调度的主程序:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Linq;
using Quartz.Impl;
using Quartz;
using Quartz.Impl.Triggers;
using System.Reflection;
using System.IO;
using Common.Logging;

namespace JobLibrary
{
    public class JobManage
    {
        private static ISchedulerFactory sf = new StdSchedulerFactory();
        private static IScheduler scheduler;
        static readonly ILog errorLog = LogManager.GetLogger("LogFileAppender");
        public static void StartScheduleFromConfig()
        {
            string currentDir = AppDomain.CurrentDomain.BaseDirectory;
            try
            {
                XDocument xDoc = XDocument.Load(Path.Combine(currentDir, "JobScheduler.config"));
                var jobScheduler = from x in xDoc.Descendants("JobScheduler") select x;

                var jobs = jobScheduler.Elements("Job");
                XElement jobDetailXElement, triggerXElement;

                scheduler = sf.GetScheduler();

                CronTriggerImpl cronTrigger;

                foreach (var job in jobs)
                {
                    //加载程序集joblibaray
                    Assembly ass = Assembly.LoadFrom(Path.Combine(currentDir, job.Element("DllName").Value));

                    jobDetailXElement = job.Element("JobDetail");
                    triggerXElement = job.Element("Trigger");

                    JobDetailImpl jobDetail = new JobDetailImpl(jobDetailXElement.Attribute("job").Value,
                                                            jobDetailXElement.Attribute("group").Value,
                                                            ass.GetType(jobDetailXElement.Attribute("jobtype").Value));

                    if (triggerXElement.Attribute("type").Value.Equals("CronTrigger"))
                    {
                        cronTrigger = new CronTriggerImpl(triggerXElement.Attribute("name").Value,
                                                        triggerXElement.Attribute("group").Value,
                                                        triggerXElement.Attribute("expression").Value);
                        scheduler.ScheduleJob(jobDetail, cronTrigger);
                    }
                }
                scheduler.Start();
            }
            catch (Exception e)
            {
                errorLog.Error(e.StackTrace);
            }
        }

        public static void ShutDown()
        {
            if (scheduler != null && !scheduler.IsShutdown)
            {
                scheduler.Shutdown();
            }
        }


        /**  
         * 从Scheduler 移除当前的Job,修改Trigger  
         *   
         * @param jobDetail  
         * @param time  
         * @throws SchedulerException  
         * @throws ParseException  
         */
        public static void ModifyJobTime(IJobExecutionContext jobExecution, String time)
        {

            scheduler = jobExecution.Scheduler;
            ITrigger trigger = jobExecution.Trigger;
            IJobDetail jobDetail = jobExecution.JobDetail;
            if (trigger != null)
            {
                CronTriggerImpl ct = (CronTriggerImpl)trigger;
                // 移除当前进程的Job   
                scheduler.DeleteJob(jobDetail.Key);
                // 修改Trigger   
                ct.CronExpressionString = time;
                Console.WriteLine("CronTrigger getName " + ct.JobName);
                // 重新调度jobDetail   
                scheduler.ScheduleJob(jobDetail, ct);
            }
        }
    }
}

                     以下是3个Job:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Common.Logging;

namespace JobLibrary
{
    public abstract class JobBase
    {
        /// <summary>
        /// JOB状态日志
        /// </summary>
        protected internal static readonly ILog jobStatus = LogManager.GetLogger("LogFileAppender");

        /// <summary>
        /// 服务错误日志
        /// </summary>
        protected internal static readonly ILog serviceErrorLog = LogManager.GetLogger("LogFileAppender");
    }
}

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Quartz;


namespace JobLibrary
{
    public class FirstJob : JobBase, IJob
    {

        #region IJob 成员

        public void Execute(IJobExecutionContext context)
        {
            jobStatus.Info("--------1111first job start ----------");
            try
            {
                jobTest();
                
            }
            catch (Exception e)
            {
                serviceErrorLog.Info(string.Concat("first job:", e.StackTrace));
            }
            jobStatus.Info("---------first job Complete ----------");
         }
        #endregion


        public void jobTest()
        {
            jobStatus.Info("weeksetting test1!");
        }

    }
}


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Quartz;


namespace JobLibrary
{
    public class SecondJob:JobBase,IJob
    {

        #region IJob 成员

        public void Execute(IJobExecutionContext context)
        {
            jobStatus.Info("------------2222 second job start -----------");
            try
            {
                jobTest();
            }
            catch (Exception e)
            {
                serviceErrorLog.Info(String.Concat("second job:", e.StackTrace));
            }
            jobStatus.Info("------------ second job Complete  -----------");
           
        }

        #endregion

        public void jobTest()
        {
            jobStatus.Info("weeksetting test2!");
        }
    }

}

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Quartz;

namespace JobLibrary
{
    public class ThirdJob : JobBase, IJob
    {
        private static int a = 1;
        #region IJob 成员

        public void Execute(IJobExecutionContext context)
        {
            jobStatus.Info("--------3333Third job start ----------");
            try
            {
                
                if (!context.JobDetail.JobDataMap.Contains("a"))
                {
                    context.JobDetail.JobDataMap.Add("a", a);
                }
                else
                {
                    context.JobDetail.JobDataMap["a"] = a;
                }
                jobTest();

                jobStatus.Info("a=" + context.JobDetail.JobDataMap["a"]);
                if (a == 3)
                {
                    JobManage.ModifyJobTime(context, "0/5 * * * * ?");
                }
                jobStatus.Info("a=" + a);
                a++;
                
            }
            catch (Exception e)
            {
                serviceErrorLog.Info(string.Concat("Third job:", e.StackTrace));
            }
            jobStatus.Info("---------Third job Complete ----------");
         }
        #endregion


        public void jobTest()
        {
            jobStatus.Info("weeksetting test3!");
        }
   
    }
}

              以下是2个配置文件App.config,log4net.config,用于配置Log的:

App.config

<?xml version="1.0"?>
<configuration>
  <configSections>
      <sectionGroup name="common">
        <section name="logging" type="Common.Logging.ConfigurationSectionHandler, Common.Logging"/>
      </sectionGroup>
  </configSections>
  <common>
    <logging>
      <!--1.此Adapter只输出到控制台--><!--
      <factoryAdapter type="Common.Logging.Simple.ConsoleOutLoggerFactoryAdapter, Common.Logging">
        <arg key="level" value="INFO" />
        <arg key="showLogName" value="true" />
        <arg key="showDataTime" value="true" />
        <arg key="dateTimeFormat" value="yyyy/MM/dd HH:mm:ss:fff" />
      </factoryAdapter>-->
      <!--2.此Adapter只输出到Log4.net的配置文件所指定的地方-->
      <factoryAdapter type="Common.Logging.Log4Net.Log4NetLoggerFactoryAdapter, Common.Logging.Log4net">
        <arg key="configType" value="FILE"/>
         FILE,FILE-WATCH,INLINE,EXTERNAL
        <arg key="configFile" value="~/log4net.config"/>   <!-- 指定log4net的配置文件名称 -->
        <arg key="level" value="Warn"/>
      </factoryAdapter>
    </logging>
  </common>
	<runtime>
		<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
			<dependentAssembly>
				<assemblyIdentity name="Common.Logging" publicKeyToken="AF08829B84F0328E" culture="neutral"/>
				<bindingRedirect oldVersion="0.0.0.0-2.1.2.0" newVersion="2.1.2.0"/>
			</dependentAssembly>
		</assemblyBinding>
	</runtime>
</configuration>

log4net.config

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <configSections>
    <section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler, log4net" />
  </configSections>
  <appSettings>
  </appSettings>
  <log4net>
    <!--定义输出到文件中-->
    <appender name="LogFileAppender"  type="log4net.Appender.RollingFileAppender">
      <!--输出日志文件的路径-->
      <file value="Log\XXLog.log" />
      <!--输出日志时自动向后追加-->
      <appendToFile value="true" />
      <!--防止多线程时不能写Log,官方说线程非安全,但实际使用时,本地测试正常,部署后有不能写日志的情况-->
      <lockingModel type="log4net.Appender.FileAppender+MinimalLock" />
      <!--置为true,当前最新日志文件名永远为file节中的名字-->
      <staticLogFileName value="false" />
      <!--日志以大小作为备份样式,还有一种方式是Date(日期)-->
      <rollingStyle value="size" />
      <countDirection value="-1" />
      <!--单个日志的最大容量,(可用的单位:KB|MB|GB)不要使用小数,否则会一直写入当前日志-->
      <maximumFileSize value="1MB" />
      <!--日志最大个数,都是最新的-->
      <maxSizeRollBackups value="10" />
      <datePattern value='"."yyyy-MM-dd".log"' />
      <layout type="log4net.Layout.PatternLayout">
        <!--每条日志末尾的文字说明-->
        <footer value="**************************************************************" />
        <!--输出格式-->
        <!--样例:2008-03-26 13:42:32,111 [10] INFO  Log4NetDemo.MainClass  - info-->
        <conversionPattern  value="%newline%d{yyyy/MM/dd,HH:mm:ss.fff},[%-5level]%newline Message:%message%newline" />
      </layout>
    </appender>
    <root>
      <!--文件形式记录日志-->
      <appender-ref ref="LogFileAppender" />
    </root>
  </log4net>
</configuration>

             使用时,在Global.asax.cs中增加这样:

 protected void Application_Start(object sender, EventArgs e)
        {
           JobManage.StartScheduleFromConfig();
         }
protected void Application_End(object sender, EventArgs e)
        {
                          JobManage.Shutdown(); 
        }

               如有新的任务加入,只需在JobScheduler.config中配置新任务项。若要停止某个任务,只需注释掉任务项,然后重启应用程序

               

application改变的条件:

1. iis重启

2. 代码改变

3. 应用程序池回收时间到了

4. 应用程序池空闲时间到了

符合以上条件后,第一个request过来时会调用Application_Start。其中3和4是可以配置的(应用程序池属性 -> 性能)


把所有的Job、JobManage主调度程序都放到一个工程JobLibrary,这样便于管理,一个Job对于一个类。要移植时,只需把该类库搬走。

你可能感兴趣的:(Quartz.net 在应用程序中统一调度各任务,Config文件配置所有的任务项)