Quartz.Net分布式任务管理平台(续)
感谢@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负载就可以了