NopCommerce之任务执行
NOP任务提供两种:手动执行(立即)和定时执行两种。
首先来说下手动任务执行过程,下图是NOP定时任务管理界面:
从上面可以看出,我们可以选择具体的任务来手动执行任务(立即执行),当点击【立即执行】按钮时会触发以下事件:
1、获取ScheduleTask对象(从数据库或缓存中)
2、创建Task对象,传入ScheduleTask对象作为构造参数,并为Task对象字段赋值
3、执行Task里的Execute()方法,该方法主要负责ITask实现类的创建,并执行Execute()方法,完成对任务的执行。
具体代码如下:
1 public ActionResult RunNow(int id) 2 { 3 if (!_permissionService.Authorize(StandardPermissionProvider.ManageScheduleTasks)) 4 return AccessDeniedView(); 5 6 try 7 { 8 var scheduleTask = _scheduleTaskService.GetTaskById(id); 9 if (scheduleTask == null) 10 throw new Exception("Schedule task cannot be loaded"); 11 12 var task = new Task(scheduleTask); 13 //ensure that the task is enabled 14 task.Enabled = true; 15 //do not dispose. otherwise, we can get exception that DbContext is disposed 16 task.Execute(true, false); 17 SuccessNotification(_localizationService.GetResource("Admin.System.ScheduleTasks.RunNow.Done")); 18 } 19 catch (Exception exc) 20 { 21 ErrorNotification(exc); 22 } 23 24 return RedirectToAction("List"); 25 }
第8行:获取ScheduleTask对象(从数据库或缓存中)
第16行:、创建Task对象,传入ScheduleTask对象作为构造参数,并为Task对象字段赋值,然后我们进入到该方法体内看下
1 public void Execute(bool throwException = false, bool dispose = true) 2 { 3 this.IsRunning = true; 4 5 //background tasks has an issue with Autofac 6 //because scope is generated each time it's requested 7 //that's why we get one single scope here 8 //this way we can also dispose resources once a task is completed 9 var scope = EngineContext.Current.ContainerManager.Scope(); 10 var scheduleTaskService = EngineContext.Current.ContainerManager.Resolve<IScheduleTaskService>("", scope); 11 var scheduleTask = scheduleTaskService.GetTaskByType(this.Type); 12 13 try 14 { 15 var task = this.CreateTask(scope); 16 if (task != null) 17 { 18 this.LastStartUtc = DateTime.UtcNow; 19 if (scheduleTask != null) 20 { 21 //update appropriate datetime properties 22 scheduleTask.LastStartUtc = this.LastStartUtc; 23 scheduleTaskService.UpdateTask(scheduleTask); 24 } 25 26 //execute task 27 task.Execute(); 28 this.LastEndUtc = this.LastSuccessUtc = DateTime.UtcNow; 29 } 30 } 31 catch (Exception exc) 32 { 33 this.Enabled = !this.StopOnError; 34 this.LastEndUtc = DateTime.UtcNow; 35 36 //log error 37 var logger = EngineContext.Current.ContainerManager.Resolve<ILogger>("", scope); 38 logger.Error(string.Format("Error while running the '{0}' schedule task. {1}", this.Name, exc.Message), exc); 39 if (throwException) 40 throw; 41 } 42 43 if (scheduleTask != null) 44 { 45 //update appropriate datetime properties 46 scheduleTask.LastEndUtc = this.LastEndUtc; 47 scheduleTask.LastSuccessUtc = this.LastSuccessUtc; 48 scheduleTaskService.UpdateTask(scheduleTask); 49 } 50 51 //dispose all resources 52 if (dispose) 53 { 54 scope.Dispose(); 55 } 56 57 this.IsRunning = false; 58 }
第15行:ITask实现类的创建,里面可能包括反射、关联对象的一些创建
第27行:执行实现了ITask类的Execute()方法,完成对任务的执行。最后更新字段。
二、第二种就是定时任务了,相对来说复杂点。
首先、在项目启动时的Application_Start()方法里,我们创建了任务管理类,
1 protected void Application_Start() 2 { 3 4 5 //start scheduled tasks 6 if (databaseInstalled) 7 { 8 TaskManager.Instance.Initialize(); 9 TaskManager.Instance.Start(); 10 } 11 }
该类主要负责任务的初始化,添加到线程列表,任务的开始和停止,如下:
1 /// <summary> 2 /// Initializes the task manager with the property values specified in the configuration file. 3 /// </summary> 4 public void Initialize() 5 { 6 this._taskThreads.Clear(); 7 8 var taskService = EngineContext.Current.Resolve<IScheduleTaskService>(); 9 var scheduleTasks = taskService 10 .GetAllTasks() 11 .OrderBy(x => x.Seconds) 12 .ToList(); 13 14 //group by threads with the same seconds 15 foreach (var scheduleTaskGrouped in scheduleTasks.GroupBy(x => x.Seconds)) 16 { 17 //create a thread 18 var taskThread = new TaskThread 19 { 20 Seconds = scheduleTaskGrouped.Key 21 }; 22 foreach (var scheduleTask in scheduleTaskGrouped) 23 { 24 var task = new Task(scheduleTask); 25 taskThread.AddTask(task); 26 } 27 this._taskThreads.Add(taskThread); 28 } 29 30 //sometimes a task period could be set to several hours (or even days). 31 //in this case a probability that it'll be run is quite small (an application could be restarted) 32 //we should manually run the tasks which weren't run for a long time 33 var notRunTasks = scheduleTasks 34 //find tasks with "run period" more than 30 minutes 35 .Where(x => x.Seconds >= _notRunTasksInterval) 36 .Where(x => !x.LastStartUtc.HasValue || x.LastStartUtc.Value.AddSeconds(x.Seconds) < DateTime.UtcNow) 37 .ToList(); 38 //create a thread for the tasks which weren't run for a long time 39 if (notRunTasks.Count > 0) 40 { 41 var taskThread = new TaskThread 42 { 43 RunOnlyOnce = true, 44 Seconds = 60 * 5 //let's run such tasks in 5 minutes after application start 45 }; 46 foreach (var scheduleTask in notRunTasks) 47 { 48 var task = new Task(scheduleTask); 49 taskThread.AddTask(task); 50 } 51 this._taskThreads.Add(taskThread); 52 } 53 }
代码分析如下:
1、清空任务线程,
2、获取数据库内所有的任务
3、创建任务线程对象
4、创建Task对象
5、将Task对象存入 Dictionary<string, Task> 包装的集合中
6、最后将线程对象taskThread存入到 由List<TaskThread>包装集合中,该集合保存了所有任务的线程,
上面执行我们将所有需要执行的任务存入到List<TaskThread>包装集合中集合,我们下面就可以针对这些任务,执行定时任务,我们看下TaskManager类中Start()方法是如何写的:
1 public void Start() 2 { 3 foreach (var taskThread in this._taskThreads) 4 { 5 taskThread.InitTimer(); 6 } 7 }
上面的代码可以看出,NOP是循环线程集合中所有任务,并执行定时任务的。
1 public void InitTimer() 2 { 3 if (this._timer == null) 4 { 5 this._timer = new Timer(new TimerCallback(this.TimerHandler), null, this.Interval, this.Interval); 6 } 7 }
上面的代码创建了Timer对象,在定时的时间内会执行TimerHandler()方法体中内容,方法体里的内容如下:
1 private void TimerHandler(object state) 2 { 3 this._timer.Change(-1, -1); 4 this.Run(); 5 if (this.RunOnlyOnce) 6 { 7 this.Dispose(); 8 } 9 else 10 { 11 this._timer.Change(this.Interval, this.Interval); 12 } 13 }
在方法体中会有个Run()方法,该方法会执行当前任务类中的Execute()方法,代码如下:
1 private void Run() 2 { 3 if (Seconds <= 0) 4 return; 5 6 this.StartedUtc = DateTime.UtcNow; 7 this.IsRunning = true; 8 foreach (Task task in this._tasks.Values) 9 { 10 task.Execute(); 11 } 12 this.IsRunning = false; 13 }
这样就实现了定时执行任务功能。。。
由于很少写博客,并且功力有限。只能这样了。。。欢迎指正