之前項目采用JAVA 的 Quartz 進行定時服調度務處理程序,目前在.NET下面使用依然可以完成相同的工作任務,其實什么語言不重要,關鍵是我們要學會利用語言實現價值。它是一個簡單的執行任務計划的組件,基本包括這三部分:Job(作業)、Trigger(觸發器)、scheduler(調度器)。
1.Job 作業:需要在任務計划中執行的具體邏輯操作
2.Trigger 觸發器:需要什么時間什么規則來去執行Job 作業
3.scheduler 調度器 :將Job 和 Trigger 注冊到 scheduler 調度器中,主要負責協調Job、Trigger 的運行
.NET可以做成服務端方式也可以做成web端處理,本方法是采用web的方式處理,話不多說直接上干活。在這里借鑒了別人的方式但是別人的有些很多漏洞和錯誤,我也進行了拋磚引玉加以完善。
首先先創建一個新項目,新建一個類庫JobLibrary項目庫:
一個Job 創建一個實例類,創建兩個實例類一個是UpdateCompleteJob.cs、UpdateAutoCancelStateJob.cs (之所以創建兩個Job是為了能方便大家了解這個組件可以同時執行多個任務)
一:UpdateCompleteJob.cs 代碼如下:
namespace JobLibrary { // quartz.net 禁止並發執行DisallowConcurrentExecution是禁止相同JobDetail同時執行,而不是禁止多個不同JobDetail同時執行。建議加上該特性,防止由於任務執行時間太長,長時間占用資源,導致其它任務堵塞。 [DisallowConcurrentExecution] public class UpdateCompleteJob : IJob { TRA_BargainOrder_Test ExpressModel; /// <summary> ///在Job 中我們必須實現IJob接口中的Execute 這個方法,才能正確的使用Job /// </summary> public async Task Execute(IJobExecutionContext context) { using (var _DbContext = new DefaultDbContext()) { //var tarorder = new TRA_BargainOrder_Test() //{ // BargainOrderCode="12345688899", // OrderStatus=1 //}; //_DbContext.TRA_BargainOrders.Add(tarorder); ////保存記錄,返回受影響的行數 //int record = _DbContext.SaveChanges(); //Console.WriteLine("添加{0}記錄成功", record); var query = _DbContext.TRA_BargainOrders.Where(c => c.OrderStatus == (int)EnumHelper.OrderStatus.Sended); //var query = _DbContext.TRA_BargainOrders.Where(c => c.OrderStatus == (int)EnumHelper.OrderStatus.Sended // && c.PayStatus == (int)EnumHelper.PayStatus.Paid).OrderBy(c => c.CreateTime).ToList().Take(20); foreach (var item in query) { if (!string.IsNullOrEmpty(item.ExpressCode)) { //根據快遞單號獲取快遞訂單信息 try { ExpressModel = await _DbContext.TRA_BargainOrders.SingleOrDefaultAsync(s => s.ExpressCode == item.ExpressCode); } catch (Exception e) { new Exception(e.Message); } //確定 已簽收 修改訂單狀態 已完成 if (ExpressModel.OrderStatus ==1&& ExpressModel.ischeck == 1) { var order = _DbContext.TRA_BargainOrders.FirstOrDefault(c => c.BargainOrderCode == item.BargainOrderCode); // var order = _DbContext.Set<TRA_BargainOrder_Test>().FirstOrDefault(c => c.BargainOrderCode == item.BargainOrderCode); order.OrderStatus = (int)EnumHelper.OrderStatus.Over; order.FlowStatus = (int)EnumHelper.FlowStatus.Over; order.UpdateTime = DateTime.Now; _DbContext.TRA_BargainOrders.Attach(order); _DbContext.Entry(order).State = EntityState.Modified; _DbContext.TRA_BargainOrders.AddOrUpdate(order); } } } //保存數據庫不能放到循環中操作 try { _DbContext.SaveChanges(); } catch (Exception E) { throw new Exception(E.Message); } } } } }
二:UpdateAutoCancelStateJob.cs 代碼如下:
namespace JobLibrary { //在Quartz.Net中,一個job(作業)即為一個類,為了讓job能在Quartz.Net的體系中執行,我們必須實現Quartz.Net提供的IJob接口的Execute方法,如本例所實現的IJob接口UpdateAutoCancelStateJob類: [DisallowConcurrentExecution] public class UpdateAutoCancelStateJob : IJob { public async Task Execute(IJobExecutionContext context) { using (var _DbContext = new DefaultDbContext()) { var order = await _DbContext.TRA_BargainOrders.FirstOrDefaultAsync(c => c.OrderStatus == (int)EnumHelper.OrderStatus.UnSend && c.PayStatus == (int)EnumHelper.PayStatus.UnPaid); if (order!=null) { if (DateDiff(DateTime.Now, order.CreateTime) > 30) { order.OrderStatus = (int)EnumHelper.OrderStatus.Cancel; order.FlowStatus = (int)EnumHelper.FlowStatus.Cancel; order.UpdateTime = DateTime.Now; _DbContext.SaveChanges(); } } } } //計算時間差的方法 private int DateDiff(DateTime DateTime1, DateTime DateTime2) { TimeSpan tss = Convert.ToDateTime(DateTime1) - Convert.ToDateTime(DateTime2); int dateDiff = Convert.ToInt32(tss.TotalMinutes); return dateDiff; } } }
以下是web啟動項目下的
三:設置Trigger 觸發器,在實際中我是將Trigger和Job 直接注冊到 scheduler 調度器中;就是需要將類庫生成的DLL 拷貝到你的需要執行的項目的文件中
觸發器的JobManage代碼如下:
public class JobManage { private static ISchedulerFactory sf = new StdSchedulerFactory(); //調度器 private static IScheduler scheduler; /// <summary> /// 讀取調度器配置文件的開始時間 /// </summary> //public static void StartScheduleFromConfig() public static async void StartScheduleFromConfigAsync() { 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 = await 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); //添加定時器 await scheduler.ScheduleJob(jobDetail, cronTrigger); } } //開始執行定時器 await scheduler.Start(); } catch (Exception E) { throw new Exception(E.Message); } } /// <summary> /// 關閉定時器 /// </summary> public static void ShutDown() { if (scheduler != null && !scheduler.IsShutdown) { scheduler.Shutdown(); } } /// <summary> /// 從Scheduler 移除當前的Job,修改Trigger /// </summary> /// <param name="jobExecution"></param> /// <param name="time"></param> 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); } } }
四:配置文件,主要是控制任務執行的時間和Job 的加載 JobScheduler.config
<?xml version="1.0" encoding="utf-8"?> <configuration> <!--配置文件,主要是控制任務執行的時間和Job 的加載 配置中重要的幾個屬性 <DllName>JobLibrary.dll</DllName> dll的名字 ;jobtype 屬性是dll名字+實例類的名字;expression 這個是設置執行的時間--> <JobScheduler> <Job Description="作業1"> <DllName>JobLibrary.dll</DllName> <JobDetail job="test1" group="test1Group" jobtype="JobLibrary.UpdateCompleteJob" /> <Trigger name="test1" group="test1Group" type="CronTrigger" expression="0 0/50 * * * ?" /> <!--0 0/10 * * * ? 10分鍾--> </Job> <Job Description="作業2"> <DllName>JobLibrary.dll</DllName> <JobDetail job="test2" group="test2Group" jobtype="JobLibrary.UpdateAutoCancelStateJob" /> <Trigger name="test2" group="test2Group" type="CronTrigger" expression="0/10 * * * * ?" /> <!--0/10 * * * * ? 10秒--> <!-- 每天凌晨1點執行一次:0 0 1 * * ? --> <!--每天凌晨1點30分執行一次:0 30 1 * * ?--> <!--每天的0點、13點、18點、21點都執行一次:0 0 0,13,18,21 * * ? --> <!-- "0 0/5 14,18 * * ?" 每天14點或18點中,每5分鍾觸發--> </Job> </JobScheduler> <system.web> <compilation debug="true" targetFramework="4.6.1" /> <httpRuntime targetFramework="4.6.1" /> </system.web> </configuration>
五:需要將scheduler 調度器注冊到程序中;在程序中Global.asax.cs 中文件中添加注冊,在這里啟動執行任務
//需要將scheduler 調度器注冊到程序中;在程序中Global.asax.cs 中文件中添加注冊,在這里啟動執行任務。 protected void Application_Start() { AreaRegistration.RegisterAllAreas(); FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters); RouteConfig.RegisterRoutes(RouteTable.Routes); BundleConfig.RegisterBundles(BundleTable.Bundles); //執行的任務 JobManage.StartScheduleFromConfigAsync(); } //當網站關閉時結束正在執行的工作 protected void Application_End(object sender, EventArgs e) { // 在應用程序關閉時運行的代碼 JobManage.ShutDown(); }
六:至此可以啟動服務完成定時調度處理任務了