我有 一個這樣的案例:有一個作業需要1個月運行一次,但是運行的時間是從數據庫中讀出來的,每個月執行的時間都不固定。比如:4月份執行的時間是4.30 18:00, 5月份執行的時間是6.2 19:00, 6月份執行的時間是6:30 18:00。一年12個月的運行時間都不固定。但是我又希望這個任務1個月運行一次,且只有一個計時器在用。
執行作業可以有很多種法子,1. MSSql Server提供了作業,可以自己創建作業來運行。2. Windows系統的計劃任務。 3. Timer計時器。 4. Quartz.net
但是這些基本都是提供按時間間隔,固定的時間點(每天,每月,每年)執行。這些任務的執行時間都是作爲配置文件在任務開始運行時就加載進去了,以後就一直按着這個點進行。若想修改調度的時間點,就只能修改配置了,然後重新啓動調度程序。但是總不能每個月都來修改一次吧。那就不叫流水作業,全人工去搞算了。
在網上搜了一些資料,有點收穫。用Quartz.net吧。發現它有個Reschedule的函數,可以重新規劃任務的執行。但是我暫時沒用。用了其他的。
Quartz.net 必須的dll有Quartz.dll,Common.Logging,Common.Logging.log4Net,C5,這些dll的版本一定要匹配。姐就深受其苦,版本不配套。整了很久。
public class QuartzManage
{
private static ISchedulerFactory sf = new StdSchedulerFactory();
private static String JOB_GROUP_NAME = "group";
private static String TRIGGER_GROUP_NAME = "trigger";
private static IScheduler sched;
public static void StartJob(String jobName, Type jobType, String time)
{
sched = sf.GetScheduler();
JobDetailImpl jobDetail = new JobDetailImpl();
jobDetail.Name = jobName;
jobDetail.Group = JOB_GROUP_NAME;
jobDetail.JobType = jobType;
CronTriggerImpl trigger = new CronTriggerImpl(jobName, TRIGGER_GROUP_NAME);
trigger.CronExpressionString = time;
sched.ScheduleJob(jobDetail, trigger);
if (!sched.IsShutdown)
{
sched.Start();
}
}
/**
* 從Scheduler 移除當前的Job,修改Trigger
*
* @param jobDetail
* @param time
* @throws SchedulerException
* @throws ParseException
*/
public static void ModifyJobTime(ITrigger trigger, IJobDetail jobDetail, String time)
{
sched = sf.GetScheduler();
if (trigger != null)
{
CronTriggerImpl ct = (CronTriggerImpl)trigger;
// 移除當前進程的Job
sched.DeleteJob(jobDetail.Key);
// 修改Trigger
ct.CronExpressionString = time;
Console.WriteLine("CronTrigger getName " + ct.JobName);
// 重新調度jobDetail
sched.ScheduleJob(jobDetail, ct);
}
}
public static void ShutDownJob()
{
if (sched != null && !sched.IsShutdown)
{
sched.Shutdown();
}
}
}
測試的Job:
public class SimpleQuartzJob:IJob
{
private static ILog _log = LogManager.GetLogger(typeof(SimpleQuartzJob));
private static int a = 1;
#region IJob 成員
public void Execute(IJobExecutionContext context)
{
try
{
a++;
string jobName = context.JobDetail.Key.Name;
_log.Info("Execution job:" + jobName + " executing at " + DateTime.Now.ToString());
Console.WriteLine("Execution job:" + jobName + " executing at " + DateTime.Now.ToString());
if (a==3)
{
QuartzManage.ModifyJobTime(context.Trigger, context.JobDetail, "0/5 * * * * ?");
}
}
catch (Exception e)
{
_log.Error("---Error in job!");
Console.WriteLine(e.StackTrace);
JobExecutionException e2 = new JobExecutionException(e);
e2.RefireImmediately = true;
throw e2;
}
}
#endregion
}
主程序
class Program
{
static void Main(string[] args)
{
Console.WriteLine(DateTime.Now.ToString());
//間隔3秒執行1次
QuartzManage.StartJob("job1", typeof(SimpleQuartzJob), "0/3 * * * * ?"); //"0 1 12 15 5 ?"
Thread.Sleep(20000);
QuartzManage.ShutDownJob();
Console.ReadKey();
}
}
配置文件App.config
<?xml version="1.0"?>
<configuration>
<configSections>
<sectionGroup name="common">
<section name="logging" type="Common.Logging.ConfigurationSectionHandler, Common.Logging"/>
</sectionGroup>
</configSections>
<common>
<logging>
<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"/>
<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 配置log的
<?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\TestLog.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>
關於前面講到的案例,可以在最初時設置4.30 18:00執行,比如在4.30 18:00執行完時,就再次設置調度的時間改爲6.2 18:00執行,這次執行完就設置下次執行的時間。
至於有什麼不好的影響。暫時不清楚。
但是這個法子有個缺陷。比如在4.30 18:00執行完時就設置下個月的執行時間6.2 18:00(從數據庫讀取的),那麼有可能用戶在5.10又將5月份的執行時間改爲5.29 18:00,而不再是6.2 18:00。調度程序已經設置了下次的調度時間,不會再去讀5月份的執行時間了。這樣就只能手動就重啓調度程序。不能一勞永逸。
思路可以借鑑。。。。。。。