偶然发现项目中有多个需要定时完成的任务,通过以下方式进行的。
<?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); } } } }
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!"); } } }
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>
<?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>
protected void Application_Start(object sender, EventArgs e) { JobManage.StartScheduleFromConfig(); }
protected void Application_End(object sender, EventArgs e) { JobManage.Shutdown(); }
application改变的条件:
1. iis重启
2. 代码改变
3. 应用程序池回收时间到了
4. 应用程序池空闲时间到了
符合以上条件后,第一个request过来时会调用Application_Start。其中3和4是可以配置的(应用程序池属性 -> 性能)
把所有的Job、JobManage主调度程序都放到一个工程JobLibrary,这样便于管理,一个Job对于一个类。要移植时,只需把该类库搬走。