感謝@Taking園友得建議,我這邊確實多做了一步上傳,導致后面還需處理同步上傳到其他服務器來支持分布式得操作。所有才有了上篇文章得完善。
首先看一下新的項目結構圖:
這個圖和上篇文章中的圖大家可以看到有不一樣的地方,那就是我們取消了任務子類。
首先我們添加任務子類的目的是想別人開發任務的時候程序集盡量的干凈,而對於我們Quartz來說真正運行的還是我們基類中的Excute方法,我們Web中需要通過反射子任務類來添加到Quartz中而現在我們通過直接反射任務基類添加到Quartz當中,這樣我們依然保持了干凈的程序集,同時我們也回歸了本質,子任務類在Quartz中並沒有起作用這一點要弄明白。
其次按原來的模式我們添加一個任務我們需要添加一個類庫然后去繼承基類然后設置url編譯,添加任務的時候上傳這將導致兩個問題,
一:當任務過多時我們項目會變的類庫特別大,每次建類庫也很麻煩
二:我們為了做分布式意味着 我們要將上傳的dll同時保存到三台服務器中的Web,和Quartz的服務節點,這個我們將需要額外的邏輯去處理麻煩。
那么現在這樣的結構無論是對編寫任務的人,還是做分布式就相對於說變得簡單的多了。
現在看一下項目得改變:首先子任務的類庫取消,任務基類改變如下:
1 public class JobBase : IJob 2 { 3 public void Execute(IJobExecutionContext context) 4 { 5 try 6 { 7 HttpClient hc = new HttpClient(); 8 hc.GetAsync(context.JobDetail.JobDataMap["requestUrl"].ToString()); 9 } 10 catch (Exception ex) 11 { 12 throw new Exception(ex.ToString()); 13 } 14 } 15 16 }
這邊取得任務被添加到Quartz時附帶得信息也就是requestUrl,這一步我們在下面任務運行得時候會看到;同時我們得添加任務得界面輸入得信息會變少:
這邊添加任務的時候跟上一篇中對比我們不需要上傳程序集,不需要程序集的名字和任務的類名,因為這些東西我們都已經知道就是我們現在的JobBase,而我們現在需要的是上篇文章中的子類設置請求地址現在放到添加任務得時候,保存到數據庫中,添加任務得代碼也會變得更簡單:
1 [HttpPost] 2 /// <summary> 3 /// 添加任務 4 /// </summary> 5 /// <param name="jobName">任務名稱</param> 6 /// <param name="jobGroupName">任務所在組名稱</param> 7 /// <param name="triggerName">觸發器名稱</param> 8 /// <param name="triggerGroupName">觸發器所在的組名稱</param> 9 /// <param name="cron">執行周期表達式</param> 10 /// <param name="jobDescription">任務描述</param> 11 /// <param name="requestUrl">請求地址</param> 12 13 /// <returns></returns> 14 15 public JsonResult AddJob(string jobName, string jobGroupName, string triggerName, string triggerGroupName, string cron, string jobDescription,string requestUrl) 16 { 17 var jobId = _customerJobInfoRepository.AddCustomerJobInfo(jobName, jobGroupName, triggerName, triggerGroupName, cron, jobDescription, requestUrl); 18 return Json(ResponseDataFactory.CreateAjaxResponseData("1", "添加成功", jobId)); 19 20 }
當任務運行得時候去除設置到Quartz中,現在運行任務得方法更改如下:
1 /// <summary> 2 /// 運行任務 3 /// </summary> 4 /// <param name="jobInfo">任務信息</param> 5 /// <returns></returns> 6 public bool RunJob(Customer_JobInfo jobInfo) 7 { 8 Assembly assembly = Assembly.LoadFile(AppDomain.CurrentDomain.BaseDirectory + $"bin/{jobInfo.DLLName}"); 9 var type = assembly.GetType(jobInfo.FullJobName); 10 JobKey jobKey = _createJobKey(jobInfo.JobName, jobInfo.JobGroupName); 11 if (!_scheduler.CheckExists(jobKey)) 12 { 13 IJobDetail job = JobBuilder.Create(type) 14 .WithIdentity(jobKey) 15 .UsingJobData(_createJobDataMap("jobId", jobInfo.Id)) 16 .UsingJobData(_createJobDataMap("requestUrl",jobInfo.RequestUrl))//添加此任務請求地址附帶到Context上下文中 17 .Build(); 18 19 CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.CronSchedule(jobInfo.Cron); 20 ITrigger trigger = TriggerBuilder.Create().StartNow()//StartAt(DateTime.SpecifyKind(jobInfo.JobStartTime, DateTimeKind.Local)) 21 .WithIdentity(jobInfo.TriggerName, jobInfo.TriggerGroupName) 22 .ForJob(jobKey) 23 .WithSchedule(scheduleBuilder.WithMisfireHandlingInstructionDoNothing()) 24 .Build(); 25 #region Quartz 任務miss之后三種操作 26 /* 27 withMisfireHandlingInstructionDoNothing 28 ——不觸發立即執行 29 ——等待下次Cron觸發頻率到達時刻開始按照Cron頻率依次執行 30 31 withMisfireHandlingInstructionIgnoreMisfires 32 ——以錯過的第一個頻率時間立刻開始執行 33 ——重做錯過的所有頻率周期后 34 ——當下一次觸發頻率發生時間大於當前時間后,再按照正常的Cron頻率依次執行 35 36 withMisfireHandlingInstructionFireAndProceed 37 ——以當前時間為觸發頻率立刻觸發一次執行 38 ——然后按照Cron頻率依次執行*/ 39 #endregion 40 41 _scheduler.ScheduleJob(job, trigger); 42 43 } 44 return true; 45 }
其實上篇我們添加監聽器時往這個里面增加的jobId,當時沒有想到把requestUrl運用上,再次感謝園友提醒。那么現在我們這次發現得問題得已解決:
一:現在對於編寫任務得人就只需要在界面操作添加requestUrl了,不需要編寫任務代碼
二:不會見到一大推子任務程序集在項目中,
三:使得分布式更加簡單只要我們的Web負載就可以了