/// <summary> /// 从job池中移除某个job,同时卸载该job所在的AppDomain /// </summary> /// <param name="jobInfo"></param> /// <returns></returns> public bool Remove(JobInfo jobInfo) { lock (Locker) { TriggerKey triKey = null; ITrigger trigger = GetTrigger(jobInfo, out triKey); if (trigger == null) { return(true); } Scheduler.PauseTrigger(triKey); Scheduler.UnscheduleJob(triKey); Scheduler.DeleteJob(GetJobKey(jobInfo)); if (JobPool.ContainsKey(jobInfo.Id)) { JobRuntimeInfo jobRuntimeInfo = null; JobPool.TryRemove(jobInfo.Id, out jobRuntimeInfo); AppDomainLoader.UnLoad(jobRuntimeInfo.AppDomain); } //TODO:记录日志 LogWrite.CreateLog().WritLog("将作业移除作业池"); return(true); } }
/// <summary> /// 更换版本 /// </summary> /// <param name="jobInfo"></param> /// <returns></returns> public bool Upgrade(JobInfo jobInfo) { lock (Locker) { Remove(jobInfo); JobRuntimeInfo jobRuntimeInfo = CreateJobRuntimeInfo(jobInfo); return(Add(jobRuntimeInfo)); } }
/// <summary> /// 恢复job /// </summary> /// <param name="jobInfo"></param> public bool Resume(JobInfo jobInfo) { //TODO:这里有两种可能 /* * 1.调度服务正常状态时恢复 * 2.调度服务挂了,重启之后,恢复job.这种情况,job池是没有job的.但是jobDetail和trigger是有的,因为我们采用的是持久化调度器,因此要特殊处理.很重要 * */ lock (Locker) { //这是第2种情况,job池没有job,属于暂停后,宕机 JobRuntimeInfo jobRuntimeInfo = null; if (!JobPool.ContainsKey(jobInfo.Id)) { JobKey jobKey = null; //如果调度任务中没有该jobDetail,那么直接返回 IJobDetail jobDetail = GetJobDetail(jobInfo, out jobKey); if (jobDetail == null) { return(false); } jobRuntimeInfo = CreateJobRuntimeInfo(jobInfo); //如果该job实例添加失败,卸载appdomain,然后返回. if (!JobPool.TryAdd(jobInfo.Id, jobRuntimeInfo)) { AppDomainLoader.UnLoad(jobRuntimeInfo.AppDomain); return(false); } else { //添加成功后,和下面那种情况一起操作了. } } //job池有job,这属于正常恢复,走下面的逻辑. //如果调度任务中没有该jobDetail 的 trigger,那么直接返回 TriggerKey triKey = null; ITrigger trigger = GetTrigger(jobInfo, out triKey); if (trigger == null) { return(false); } Scheduler.ResumeTrigger(triKey).Wait(); return(true); //TODO:记录日志 } }
/// <summary> /// 获取已添加到job池中的job /// </summary> /// <param name="jobId"></param> /// <returns></returns> internal JobRuntimeInfo GetJobFromPool(int jobId) { if (!JobPool.ContainsKey(jobId)) { return(null); } lock (Locker) { if (!JobPool.ContainsKey(jobId)) { return(null); } JobRuntimeInfo jobRuntimeInfo = null; JobPool.TryGetValue(jobId, out jobRuntimeInfo); return(jobRuntimeInfo); } }
/// <summary> /// job池没有该job时,创建 job,并开始调度 /// TODO:注意,虽然job池没有该job,但是trigger和jobDetail是有的 /// </summary> /// <param name="jobInfo"></param> public bool CreateJob(JobInfo jobInfo) { if (jobInfo == null) { return(false); } if (JobPool.ContainsKey(jobInfo.Id)) { return(false); } lock (Locker) { if (JobPool.ContainsKey(jobInfo.Id)) { return(false); } JobRuntimeInfo jobRuntimeInfo = CreateJobRuntimeInfo(jobInfo); return(Add(jobRuntimeInfo)); } }
/// <summary> /// 添加job到job池,同时加入到调度任务中. /// </summary> /// <param name="jobRuntimeInfo"></param> /// <returns></returns> internal bool Add(JobRuntimeInfo jobRuntimeInfo) { lock (Locker) { try { //如果该job实例添加失败,卸载该job的appdomain,然后返回. if (!JobPool.TryAdd(jobRuntimeInfo.JobInfo.Id, jobRuntimeInfo)) { AppDomainLoader.UnLoad(jobRuntimeInfo.AppDomain); return(false); } JobKey jobKey = null; //如果调度任务中已经有该jobDetail,则直接删掉 IJobDetail jobDetail = GetJobDetail(jobRuntimeInfo.JobInfo, out jobKey); if (jobDetail != null) { Scheduler.DeleteJob(jobKey).Wait(); } jobDetail = CreateJobDetail(jobRuntimeInfo.JobInfo); ITrigger trigger = CreateTrigger(jobRuntimeInfo.JobInfo); Scheduler.ScheduleJob(jobDetail, trigger).Wait(); //TODO:记录日志 return(true); } catch (Exception ex) { JobRuntimeInfo jri = null; //异常了,直接从job池移除该job,不再考虑移除失败的情况.考虑不到那么多了 if (JobPool.TryRemove(jobRuntimeInfo.JobInfo.Id, out jri)) { //成功移除后,再卸载掉应用程序域,失败则不移除,保留. AppDomainLoader.UnLoad(jobRuntimeInfo.AppDomain); } throw ex; } } }
/// <summary> /// 创建新的应用程序域,返回运行时的Job数据 /// </summary> /// <param name="jobInfo"></param> internal JobRuntimeInfo CreateJobRuntimeInfo(JobInfo jobInfo) { lock (Locker) { //JobRuntimeInfo jobRuntimeInfo = null; //if (JobPool.ContainsKey(jobInfo.Id)) //{ // jobRuntimeInfo = GetJobFromPool(jobInfo.Id); // return jobRuntimeInfo; //} AppDomain app = Thread.GetDomain(); BaseJob.BaseJob job = AppDomainLoader.Load(jobInfo.AssemblyPath, jobInfo.ClassType, out app); JobRuntimeInfo jobRuntimeInfo = new JobRuntimeInfo { JobInfo = jobInfo, Job = job, AppDomain = app, }; //TODO:日志记录 return(jobRuntimeInfo); } }
/// <summary> /// 线程池有job,但是该job的应用程序域已经卸载(一般都是宕机),替换job池中的jobRuntimeInfo,并重新调度该job /// </summary> /// <param name="jobRuntimeInfo"></param> /// <returns></returns> internal bool ReplaceJobRuntimeInfo(JobRuntimeInfo jobRuntimeInfo) { //TODO:有BUG,没有地方还原 _flag 的值 //if (_flag) //{ // return true; //} lock (Locker) { //if (_flag) //{ // return true; //} AppDomain app = Thread.GetDomain(); jobRuntimeInfo.Job = AppDomainLoader.Load(jobRuntimeInfo.JobInfo.AssemblyPath, jobRuntimeInfo.JobInfo.ClassType, out app); jobRuntimeInfo.AppDomain = app; JobPool[jobRuntimeInfo.JobInfo.Id] = jobRuntimeInfo; //_flag = true; return(true); } }
public Task Execute(IJobExecutionContext context) { try { JobInfo jobInfo = context.JobDetail.JobDataMap.Get("jobInfo") as JobInfo ?? new JobInfo(); //从job池中查找逻辑job JobRuntimeInfo jobRuntimeInfo = SchedulerManager.Singleton.GetJobFromPool(jobInfo.Id); try { if (jobRuntimeInfo != null) { try { bool runRes = jobRuntimeInfo.Job.Run(); if (runRes == false) { Exception ex = new Exception("作业内部发生异常"); LogWriter.WriteLog(ex, $"作业名称 : {jobInfo.JobName}"); } } //如果因为某种原因 appdomain 被卸载会进入catch块,重新创建 appdomain catch (AppDomainUnloadedException ex) { LogWriter.WriteLog(ex, "appdomain 被卸载,准备重新加载"); SchedulerManager.Singleton.ReplaceJobRuntimeInfo(jobRuntimeInfo); } } //如果job池没有该job //TODO:注意,逻辑走到这里一般都是宕机了,因为job池是在内存中,所以重启调度服务后,肯定是没有的.而jobDetail和trigger是在数据库中,用的是官方的持久化方案. else { try { bool creRes = SchedulerManager.Singleton.CreateJob(jobInfo); //重新创建job成功后,需要手动执行一次. if (creRes == true) { jobRuntimeInfo = SchedulerManager.Singleton.GetJobFromPool(jobInfo.Id); jobRuntimeInfo.Job.Run(); } } catch (Exception ex) { LogWriter.WriteLog(ex, $"重新创建job失败:{JsonConvert.SerializeObject(jobInfo)}"); } } } catch (Exception ex) { //写日志,job调用失败,或者job执行的wcf程序抛出异常 LogWriter.WriteLog(ex, $"job执行失败 : {JsonConvert.SerializeObject(jobInfo)}"); } } catch (Exception ex) { //TODO:调用的时候失败属于系统级错误,非常重要,写日志! LogWriter.WriteLog(ex, $"系统级错误,{nameof(Execute)}"); } return(Task.FromResult(0)); }