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         }

这样就实现了定时执行任务功能。。。

由于很少写博客,并且功力有限。只能这样了。。。欢迎指正


原文地址:https://www.cnblogs.com/xujie520/p/5150433.html