一個任務調度


最近把以前項目中用的任務調度提了出來,做了一個Demo。

 

任務調度用到的組件是quartz.net。關於quartz.net的文章網上有很多了,這里再簡單介紹下。

首先是創建一個作業明細

 /// <summary>
        /// 根據作業計划來創建作業明細
        /// </summary>
        /// <param name="task"></param>
        /// <param name="taskData"></param>
        /// <returns></returns>
        public static IJobDetail CreateJobDetail(ScheduleTask task, IDictionary<string, object> taskData = null)
        {
            if (task == null) throw new ArgumentNullException("『CreateJobDetail』的task參數為空!");
            //反射加載程序集
            var path = System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, string.Format("bin\\TaskManageDemo.Task.dll"));
            Assembly asmb = Assembly.LoadFrom(path);
            Type taskType = asmb.GetType(string.Format("TaskManageDemo.Task.{0}", task.MethodName)); 
            if (taskType == null)
                throw new NotImplementedException(string.Format("『{0}』調用的類型未實現", task.MethodName));

            //作業執行上下文攜帶數據
            IDictionary<string, object> map = new Dictionary<string, object>() { { "task", task } };
           
            IJobDetail job =
                 JobBuilder.Create(taskType)
                .WithDescription(task.Remark)
                .WithIdentity(GetJobKey(task))
                .UsingJobData(new JobDataMap(map)) 
                .Build();

            return job;
        }
View Code

然后是創建一個觸發器

 /// <summary>
        /// 根據作業計划來創建作業觸發器
        /// </summary>
        /// <param name="task"></param>
        /// <param name="forJob"></param>
        /// <returns></returns>
        public static ITrigger CreateTrigger(ScheduleTask task, IJobDetail forJob = null)
        {
            if (task == null) throw new ArgumentNullException("『CreateTrigger』的task參數為空!");

            TriggerBuilder trigger = TriggerBuilder.Create();
            trigger.WithDescription(task.Remark)
             .WithIdentity(GetTriggerKey(task));

            trigger = string.IsNullOrEmpty(task.CronExpression) ?
                trigger.WithSimpleSchedule(x => x.WithIntervalInMinutes(30).WithRepeatCount(10)) :
                trigger.WithCronSchedule(task.CronExpression);

            if (forJob != null) trigger.ForJob(forJob);

            return trigger.Build();

        }
View Code

最后把創建的兩樣組合起來就新增了一個作業了

/// <summary>
        /// 使用作業計划來創建作業
        /// </summary>
        /// <param name="quartzScheduler">調度器</param>
        /// <param name="task">作業任務</param>
        /// <returns>二元組</returns>
        public static Tuple<IJobDetail, ITrigger> CreatScheduleJob(IScheduler quartzScheduler, ScheduleTask task)
        {
            Tuple<IJobDetail, ITrigger> tuple = null;
            IJobDetail ij = CreateJobDetail(task);
            ITrigger it = CreateTrigger(task);

            quartzScheduler.ScheduleJob(ij, it);
            tuple = new Tuple<IJobDetail, ITrigger>(ij, it);

            return tuple;
        }
View Code

作業必須實現IJob接口

這里用了一個基類來實現IJob接口,基類里有一個抽象方法,其他作業子類只要繼承這個基類並實現這個抽象方法就行了。

public void Execute(IJobExecutionContext context)
        {
            //Quartz.Collection.ISet<JobKey> jobKeys = context.Scheduler.GetJobKeys(
            //      Quartz.Impl.Matchers.GroupMatcher<JobKey>.GroupEquals(JobHelp.jobGroupName));    //取所有運行的作業

            var task = context.MergedJobDataMap["task"] as ScheduleTask;
            string message = string.Format("{0}的『Execute』從『IJobExecutionContext』讀取不到作業計划!", ""); // this.FullDevName
            if (task == null) throw new Exception(message);

            //刷新作業計划信息,防止作業計划配置發生改變
            string sql = "select * from ScheduleTask where id=@id";
            var taskNew = _scheduleTask.GetFirst<ScheduleTask>(sql, new { id = task.Id });

            if (taskNew == null)
            {
                //計划已經被刪除,則刪除此作業
                context.Scheduler.DeleteJob(context.JobDetail.Key);
                Log.Logger.InfoFormat(string.Format("{0}作業計划為空,該記錄可能已經被刪除。", taskNew.MethodName));
                return; //退出
            }

            //作業不允許使用
            if (!taskNew.Allowused)
            {
                //不從調度計划中刪除本作業,因為有可能又啟用該作業計划
                Log.Logger.InfoFormat(string.Format("{0}作業計划不允許使用,跳過此次執行。", taskNew.MethodName));
                return; //退出
            }
            if (taskNew != task)
            {

                //臟數據,刪除此作業,然后重新創建一個
                Log.Logger.InfoFormat("{0}的作業計划屬性已更改,將刪除該計划的實現作業,然后重新創建一個作業,並嘗試調度它...", taskNew.MethodName);
                //作業計划屬性發生變更,重新啟動作業
                Tuple<IJobDetail, ITrigger> tuple = JobHelp.RestartJob(context.Scheduler, task, taskNew);
                Log.Logger.InfoFormat("{0}重新創建並調度作業完成,『IJOB.Execute』退出。作業計划:{1},作業:{2},觸發器:{3},表達式:{4}。", taskNew.MethodName, taskNew.MethodName, tuple.Item1.Key.Name, tuple.Item2.Key.Name, taskNew.CronExpression);
                return; //退出
            }
            
            //執行具體作業的業務邏輯
            ExecuteJobImpl(context);

            //更新執行時間
            taskNew.LastTime=DateTime.Now.ToString();
            taskNew.NextTime = string.Format("{0:G}", context.NextFireTimeUtc.Value.AddHours(8));
            UpdateTime(taskNew);
        }

新增作業

怎樣新增一個作業呢?

新增一個作業也很簡單,只需在TaskManageDemo.Task項目下添加一個類,並繼承作業父類實現抽象方法就可以了。

那怎樣來配置呢?

實現了作業后,點擊新增按鈕來增加一個作業。

新增的作業是怎樣被觸發的呢?  

這里我用了一個系統作業來喚醒新增的作業和刪除不需要的作業。

protected override void ExecuteJobImpl(Quartz.IJobExecutionContext context)
        {
            //取所有運行的作業
            Quartz.Collection.ISet<JobKey> jobKeys = context.Scheduler.GetJobKeys(
                   Quartz.Impl.Matchers.GroupMatcher<JobKey>.GroupEquals(JobHelp.jobGroupName));

            string sql = "select * from ScheduleTask where ClassName!=@ClassName";
            var taskRuning = new List<ScheduleTask>();//正在運行的作業
            var taskInDb = _scheduleTask.Query<ScheduleTask>(sql, new { ClassName = "SysJob" }); //存在於數據庫的作業,不包括系統作業
            foreach (var jk in jobKeys)
            {
                IJobDetail job = context.Scheduler.GetJobDetail(jk);
                ScheduleTask task = job.JobDataMap["task"] as ScheduleTask;
                if (task == null || task.ClassName == "SysJob") continue;  //不檢查系統作業  

                //在數據庫檢測一次
                var taskInDb2 = taskInDb.ToList().FirstOrDefault(a => a.Id == task.Id);
                if (taskInDb2 == null)
                {
                    context.Scheduler.DeleteJob(jk); //刪除該作業
                    Log.Logger.InfoFormat("作業計划『{0}』已經不存在於數據庫。", task.ClassName);
                    continue;
                }
                taskRuning.Add(taskInDb2);

                if (taskInDb2 != task)
                {

                    //臟數據,刪除此作業,然后重新創建一個
                    Log.Logger.InfoFormat("{0}的作業計划屬性已更改,將刪除該計划的實現作業,然后重新創建一個作業,並嘗試調度它...", taskInDb2.ClassName);
                    //作業計划屬性發生變更,重新啟動作業
                    Tuple<IJobDetail, ITrigger> tuple = JobHelp.RestartJob(context.Scheduler, task, taskInDb2);
                    Log.Logger.InfoFormat("{0}重新創建並調度作業完成,『IJOB.Execute』退出。作業計划:{1},作業:{2},觸發器:{3},表達式:{4}。", taskInDb2.ClassName, taskInDb2.ClassName, tuple.Item1.Key.Name, tuple.Item2.Key.Name, taskInDb2.CronExpression);
                    return; //退出
                }
            }
            //過濾出新增的作業
            var newTask = taskInDb.Except(taskRuning);
            if (newTask.Count() > 0)
            {
                //動態增加作業
                Log.Logger.InfoFormat("系統作業檢測到有{0}個新增作業計划,開始創建這些作業...", newTask.Count());
                foreach (var task in newTask)
                    TaskManageDemo.Utility.Common.Execute(ScheduleJobByPlan, context.Scheduler, task, "創建作業失敗,作業計划名稱:{0}", task.ClassName); 

                Log.Logger.InfoFormat("系統作業創建作業完畢,共創建{0}個作業。", context.Scheduler.GetJobKeys(Quartz.Impl.Matchers.GroupMatcher<JobKey>.GroupEquals(JobHelp.jobGroupName)).Count - 1);
            }
            //系統作業執行完畢
            Log.Logger.InfoFormat("系統輪詢作業執行完畢,目前共{0}個作業正在運行。", context.Scheduler.GetJobKeys(Quartz.Impl.Matchers.GroupMatcher<JobKey>.GroupEquals(JobHelp.jobGroupName)).Count);
            context.Put("ExecResult", "系統作業執行完成。");

            //Log.Logger.ErrorFormat("本次運行時間{0},下次運行時間{1}", DateTime.Now.ToString(), context.NextFireTimeUtc.Value.DateTime.AddHours(8).ToString());
        }

 

主要的應該也就這些,當然這是個Demo,有很多地方不是很完善。

源碼下載

https://git.oschina.net/bin88123/TaskManageDemo


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM