web下计划任务的实现方式

目录

    一、前言

    二、解决思路

    三、核心类图UML和依赖关系

    四、源代码

    五、运行结果

    六、总结

一、前言

       前两天在QQ群里大家又讨论起了在web下怎么做任务?大部分的群友提供的思路都是做windows服务。那么除了做windows服务以外,还有没有其他的思路或者解决方案呢?

       今天和大家分享的是我在开源代码里看到的一部分关于web下实现任务的核心代码,在此抛砖引玉。

       使用的技术和框架 : NetFramework4.5、C#、MVC4、log4net

二、解决思路

       Net中提供的计时器中有两种:阻塞式定时器和非阻塞式定时器,在web中要达到定时完成某项任务,就需要用到非阻塞式的定时器,net提供的命名空间是:System.Threading,类是Timer,定时器会根据指定的间隔时间,定时指定任务代码。

三、核心类图UML和依赖关系

类图:

49bec40a-a660-47f6-b460-c583918bbfd5

ITask是任务接口,定义类实行方法,是整个模块的核心接口

ScheduleTask是任务计划类,提供需要做计划任务的数据,ScheduleTaskList是提供任务计划的数据源,真实环境是需要数据库提供数据源

ITaskFactory是任务计划的工厂接口,TaskFactory继承ITaskFactory,根据提供的任务类型创建具体的任务

Task是任务执行结果类,实现了任务的运行和运行之后的状态数据如何保存(Demo中并为实现)

TaskThread是核心类,主要是定义定时器和维护任务列表,

TaskManager是核心类,主要是管理任务,提供初始化和开始方法,维护TaskThread列表

 

引用关系图:

6b0864ab-a8cf-4c23-bb5b-8f7fa55ea45a

LogTask实现ITask接口

ITaskFactory、TaskFactory、Task依赖ITask接口

TaskFactory实现ITaskFactory,依赖LogTask

Task依赖ITask、ITaskFactory、TaskFactory、ScheduleTask、ScheduleTaskList

ScheduleTaskList依赖SecheduleTask

TaskThread依赖Task

TaskManager依赖ScheduleTask、ScheduleTaskList、Task、TaskThread

 

四、源代码说明:

 

ITask:任务接口

 1 /// <summary>
 2     /// Interface that should be implemented by each task
 3     /// </summary>
 4     public interface ITask
 5     {
 6         #region Methods
 7 
 8         /// <summary>
 9         /// Execute task
10         /// </summary>
11         void Execute();
12 
13         #endregion
14     }
ITask

说明:声明了任务运行方法

 

ScheduleTask类:计划任务类   

 1 /// <summary>
 2     /// The schedule task.
 3     /// 计划任务,这里可以作为数据库的Model,将需要执行的任务保存到数据库
 4     /// </summary>
 5     public class ScheduleTask
 6     {
 7         #region Properties
 8 
 9         /// <summary>
10         /// Gets or sets the name
11         /// 任务名称
12         /// </summary>
13         public string Name { get; set; }
14 
15         /// <summary>
16         /// Gets or sets the run period (in seconds)
17         /// 执行间隔时间,单位毫秒
18         /// </summary>
19         public int Seconds { get; set; }
20 
21         /// <summary>
22         /// Gets or sets the type of appropriate ITask class
23         /// 任务类型:这里如果采用IOC,保存到数据库的格式是:namespace,程序集.dll
24         /// </summary>
25         public string Type { get; set; }
26 
27         /// <summary>
28         /// Gets or sets a value indicating whether enabled.
29         /// 任务是否启用
30         /// </summary>
31         public bool Enabled { get; set; }
32 
33         /// <summary>
34         /// Gets or sets a value indicating whether stop on error.
35         /// </summary>
36         public bool StopOnError { get; set; }
37 
38         /// <summary>
39         /// Gets or sets the last start.
40         /// 最后开始时间
41         /// </summary>
42         public DateTime? LastStart { get; set; }
43 
44         /// <summary>
45         /// Gets or sets the last end.
46         /// 最后结束时间
47         /// </summary>
48         public DateTime? LastEnd { get; set; }
49 
50         /// <summary>
51         /// Gets or sets the last success.
52         /// 最后执行成功时间
53         /// </summary>
54         public DateTime? LastSuccess { get; set; }
55 
56         #endregion
57     }
ScheduleTask

说明:主要包含了几个属性

 

ScheduleTaskList:提供计划任务数据源 

 1 /// <summary>
 2     /// The schedule task list.
 3     /// </summary>
 4     public class ScheduleTaskList : List<ScheduleTask>
 5     {
 6         /// <summary>
 7         /// Prevents a default instance of the <see cref="ScheduleTaskList"/> class from being created.
 8         /// </summary>
 9         private ScheduleTaskList()
10         {
11             this.Add(new ScheduleTask
12                          {
13                              Enabled = true, 
14                              Name = "定时记录日志", 
15                              Seconds = 30 * 1000, 
16                              Type = "logTask"
17                          });
18         }
19 
20         /// <summary>
21         /// The get schedule task by type.
22         /// </summary>
23         /// <param name="taskType">
24         /// The task type.
25         /// </param>
26         /// <returns>
27         /// The <see cref="ScheduleTask"/>.
28         /// </returns>
29         public static ScheduleTask GetScheduleTaskByType(string taskType)
30         {
31             return new ScheduleTaskList().SingleOrDefault(u => u.Type == taskType);
32         }
33 
34         /// <summary>
35         /// The get all schedule task.
36         /// </summary>
37         /// <returns>
38         /// The <see>
39         ///         <cref>IEnumerable</cref>
40         ///     </see>
41         ///     .
42         /// </returns>
43         public static IEnumerable<ScheduleTask> GetAllScheduleTask()
44         {
45             return new ScheduleTaskList();
46         }
47     }
ScheduleTaskList

说明:提供数据源,GetSchedulaTask是根据任务类型获取到计划任务,GetAllScheduleTask是获取所有的计划任务,这里主要为任务管理器提供所有的计划任务

 

ITaskFactory:Task工厂接口

 1 /// <summary>
 2     /// The TaskFactorycs interface.
 3     /// 任务工厂,即使使用可以使用IOC替换,不需要使用工厂类
 4     /// </summary>
 5     public interface ITaskFactory
 6     {
 7         /// <summary>
 8         /// The create task.
 9         /// </summary>
10         /// <param name="taskType">
11         /// The task type.
12         /// </param>
13         /// <returns>
14         /// The <see cref="ITask"/>.
15         /// </returns>
16         ITask CreateTask(string taskType);
17     }
ITaskFactory

说明:提供CreateTask方法,根据task类型创建具体需要执行的任务

 

TaskFactory:任务工厂实现,主要实现ITaskFactory接口   

 1 /// <summary>
 2     /// The task factory.
 3     /// 任务工厂实现
 4     /// </summary>
 5     public class TaskFactory : ITaskFactory
 6     {
 7         /// <summary>
 8         /// The create task.
 9         /// </summary>
10         /// <param name="taskType">
11         /// The task type.
12         /// </param>
13         /// <returns>
14         /// The <see cref="ITask"/>.
15         /// </returns>
16         public ITask CreateTask(string taskType)
17         {
18             if (taskType == "logTask")
19             {
20                 return new LogTask();
21             }
22 
23             return null;
24         }
25     }
TaskFactory

说明:这个工厂是真实环境中替代IOC的,此处主要创建了LogTask

 

LogTask:具体需要执行的任务,这里调用了Log4Net 记录日志

 1 /// <summary>
 2     /// The log task.
 3     /// </summary>
 4     public class LogTask : ITask
 5     {
 6         #region ITask
 7 
 8         /// <summary>
 9         /// The execute.
10         /// </summary>
11         public void Execute()
12         {
13             // TODO:具体任务干的事情
14             MvcApplication.Log().Info("具体任务做的事情");
15         }
16 
17         #endregion
18     }
LogTask

说明:这个类就是具体要执行的任务了,任务执行的具体内容都卸载了Execute方法内

 

Task:任务类

  1 /// <summary>
  2     /// Task
  3     /// 任务类
  4     /// </summary>
  5     public class Task
  6     {
  7         #region Ctor
  8 
  9         /// <summary>
 10         /// Initializes a new instance of the <see cref="Task"/> class.
 11         /// </summary>
 12         /// <param name="task">
 13         /// The task.
 14         /// </param>
 15         public Task(ScheduleTask task)
 16         {
 17             this.Type = task.Type;
 18             this.Enabled = task.Enabled;
 19             this.StopOnError = task.StopOnError;
 20             this.Name = task.Name;
 21         }
 22 
 23 
 24         /// <summary>
 25         /// Prevents a default instance of the <see cref="Task"/> class from being created.
 26         /// </summary>
 27         private Task()
 28         {
 29             this.Enabled = true;
 30         }
 31 
 32 
 33         #endregion
 34 
 35         #region Properties
 36 
 37         /// <summary>
 38         /// 是否正在运行
 39         /// </summary>
 40         public bool IsRunning { get; private set; }
 41 
 42         /// <summary>
 43         /// 任务最后开始时间
 44         /// </summary>
 45         public DateTime? LastStart { get; private set; }
 46 
 47         /// <summary>
 48         /// 任务最后结束时间
 49         /// </summary>
 50         public DateTime? LastEnd { get; private set; }
 51 
 52         /// <summary>
 53         /// 任务最后执行成功时间
 54         /// </summary>
 55         public DateTime? LastSuccess { get; private set; }
 56 
 57         /// <summary>
 58         /// 任务类型
 59         /// </summary>
 60         public string Type { get; private set; }
 61 
 62         /// <summary>
 63         /// 是否因为发生错误而停止
 64         /// </summary>
 65         public bool StopOnError { get; private set; }
 66 
 67         /// <summary>
 68         /// 任务名称
 69         /// </summary>
 70         public string Name { get; private set; }
 71 
 72         /// <summary>
 73         /// 是否启用
 74         /// </summary>
 75         public bool Enabled { get; set; }
 76 
 77         #endregion
 78 
 79 
 80         #region Methods
 81 
 82         /// <summary>
 83         /// The execute.
 84         /// 执行任务
 85         /// </summary>
 86         /// <param name="throwException">
 87         /// The throw exception.
 88         /// </param>
 89         /// <param name="dispose">
 90         /// The dispose.
 91         /// </param>
 92         public void Execute(bool throwException = false, bool dispose = true)
 93         {
 94             this.IsRunning = true;
 95 
 96             var scheduleTask = ScheduleTaskList.GetScheduleTaskByType(this.Type);
 97 
 98             try
 99             {
100                 ITask task = this.CreateTask(this.Type, new TaskFactory());
101 
102                 if (task != null)
103                 {
104                     this.LastStart = DateTime.Now;
105                     if (scheduleTask != null)
106                     {
107                         // update appropriate datetime properties
108                         scheduleTask.LastStart = this.LastStart;
109                         // 更新任务计划执行情况
110                         //scheduleTaskService.Update(scheduleTask); 
111                     }
112 
113                     task.Execute();
114                     this.LastEnd = this.LastSuccess = DateTime.UtcNow;
115                 }
116             }
117             catch (Exception exc)
118             {
119                 this.Enabled = !this.StopOnError;
120                 this.LastEnd = DateTime.UtcNow;
121             }
122 
123             if (scheduleTask != null)
124             {
125                 scheduleTask.LastEnd = this.LastEnd;
126                 scheduleTask.LastSuccess = this.LastSuccess;
127                 // 更新任务计划执行情况
128                 //scheduleTaskService.Update(scheduleTask); 
129             }
130 
131             this.IsRunning = false;
132         }
133 
134         /// <summary>
135         /// The create task.
136         /// </summary>
137         /// <param name="taskType">
138         /// The task type.
139         /// </param>
140         /// <param name="taskFactory">
141         /// The task factory.
142         /// </param>
143         /// <returns>
144         /// The <see cref="ITask"/>.
145         /// </returns>
146         /// <exception cref="ArgumentNullException">
147         /// </exception>
148         private ITask CreateTask(string taskType, ITaskFactory taskFactory)
149         {
150             if (taskFactory == null)
151             {
152                 throw new ArgumentNullException("taskFactory");
153             }
154 
155             ITask task = null;
156             if (this.Enabled)
157             {
158                 // Demo中采用了任务工厂的方式,实际过程中采用IOC
159                 task = taskFactory.CreateTask(taskType);
160             }
161 
162             return task;
163         }
164 
165         #endregion
166     }
Task

说明:这里主要是获取到具体的任务,然后执行任务,并且把任务执行结果保存起来

 

TaskThread:任务线程类 

  1 /// <summary>
  2     /// Represents task thread
  3     /// </summary>
  4     public class TaskThread : IDisposable
  5     {
  6         #region Fields
  7 
  8         /// <summary>
  9         /// The timer.
 10         /// 定时器
 11         /// </summary>
 12         private Timer timer;
 13 
 14         /// <summary>
 15         /// The disposed.
 16         /// 知否已销毁
 17         /// </summary>
 18         private bool disposed;
 19 
 20         /// <summary>
 21         /// The tasks.
 22         /// 任务字典
 23         /// </summary>
 24         private readonly Dictionary<string, Task> tasks;
 25 
 26         #endregion
 27 
 28         #region Ctor
 29 
 30         /// <summary>
 31         /// Initializes a new instance of the <see cref="TaskThread"/> class.
 32         /// </summary>
 33         internal TaskThread()
 34         {
 35             this.tasks = new Dictionary<string, Task>();
 36             this.Seconds = 10 * 60;
 37         }
 38 
 39         #endregion
 40 
 41 
 42         #region Peoperties
 43 
 44         /// <summary>
 45         /// Gets or sets the interval in seconds at which to run the tasks
 46         /// </summary>
 47         public int Seconds { get; set; }
 48 
 49         /// <summary>
 50         /// Get or sets a datetime when thread has been started
 51         /// </summary>
 52         public DateTime Started { get; private set; }
 53 
 54         /// <summary>
 55         /// Get or sets a value indicating whether thread is running
 56         /// </summary>
 57         public bool IsRunning { get; private set; }
 58 
 59         /// <summary>
 60         /// Get a list of tasks
 61         /// </summary>
 62         public IList<Task> Tasks
 63         {
 64             get
 65             {
 66                 var list = new List<Task>();
 67                 foreach (var task in this.tasks.Values)
 68                 {
 69                     list.Add(task);
 70                 }
 71 
 72                 return new ReadOnlyCollection<Task>(list);
 73             }
 74         }
 75 
 76         /// <summary>
 77         /// Gets the interval at which to run the tasks
 78         /// </summary>
 79         public int Interval
 80         {
 81             get
 82             {
 83                 return this.Seconds;
 84             }
 85         }
 86 
 87         /// <summary>
 88         /// Gets or sets a value indicating whether the thread whould be run only once (per appliction start)
 89         /// </summary>
 90         public bool RunOnlyOnce { get; set; }
 91 
 92         #endregion
 93 
 94 
 95 
 96         #region IDisposable
 97 
 98         /// <summary>
 99         /// Disposes the instance
100         /// </summary>
101         public void Dispose()
102         {
103             if ((this.timer == null) || this.disposed)
104             {
105                 return;
106             }
107 
108             lock (this)
109             {
110                 this.timer.Dispose();
111                 this.timer = null;
112                 this.disposed = true;
113             }
114         }
115 
116         #endregion
117 
118 
119         /// <summary>
120         /// Inits a timer
121         /// 初始化定时器
122         /// </summary>
123         public void InitTimer()
124         {
125             if (this.timer == null)
126             {
127                 this.timer = new Timer(new TimerCallback(this.TimerHandler), null, this.Interval, this.Interval);
128             }
129         }
130 
131 
132         /// <summary>
133         /// Adds a task to the thread
134         /// </summary>
135         /// <param name="task">
136         /// The task to be added
137         /// </param>
138         public void AddTask(Task task)
139         {
140             if (!this.tasks.ContainsKey(task.Name))
141             {
142                 this.tasks.Add(task.Name, task);
143             }
144         }
145 
146         /// <summary>
147         /// The run.
148         /// </summary>
149         private void Run()
150         {
151             if (this.Seconds <= 0)
152             {
153                 return;
154             }
155 
156             this.Started = DateTime.Now;
157             this.IsRunning = true;
158             foreach (Task task in this.tasks.Values)
159             {
160                 task.Execute();
161             }
162            
163             this.IsRunning = false;
164         }
165 
166         /// <summary>
167         /// The timer handler.
168         /// </summary>
169         /// <param name="state">
170         /// The state.
171         /// </param>
172         private void TimerHandler(object state)
173         {
174             this.timer.Change(-1, -1);
175             this.Run();
176             if (this.RunOnlyOnce)
177             {
178                 this.Dispose();
179             }
180             else
181             {
182                 this.timer.Change(this.Interval, this.Interval);
183             }
184         }
185     }
TaskThread

说明:这个类主要是靠Timer定时器来实现的,初始化定时器以及需要执行的任务,

 

TaskManager:任务计划的管理器  

  1 /// <summary>
  2     /// Represents task manager
  3     /// </summary>
  4     public class TaskManager
  5     {
  6         #region Fields
  7 
  8         /// <summary>
  9         /// The task manager.
 10         /// </summary>
 11         private static readonly TaskManager taskManager = new TaskManager();
 12 
 13         /// <summary>
 14         /// The _task threads.
 15         /// </summary>
 16         private readonly List<TaskThread> taskThreads = new List<TaskThread>();
 17 
 18         /// <summary>
 19         /// The _not run tasks interval.
 20         /// </summary>
 21         private const int NotRunTasksInterval = 60 * 30; // 30 minutes
 22 
 23         #endregion
 24 
 25         #region Ctor
 26 
 27         /// <summary>
 28         /// Prevents a default instance of the <see cref="TaskManager"/> class from being created.
 29         /// </summary>
 30         private TaskManager()
 31         {
 32         }
 33 
 34         #endregion
 35 
 36         #region Properties
 37 
 38         /// <summary>
 39         /// Gets the task mamanger instance
 40         /// </summary>
 41         public static TaskManager Instance
 42         {
 43             get
 44             {
 45                 return taskManager;
 46             }
 47         }
 48 
 49         /// <summary>
 50         /// Gets a list of task threads of this task manager
 51         /// 任务线程列表
 52         /// </summary>
 53         public IList<TaskThread> TaskThreads
 54         {
 55             get
 56             {
 57                 return new ReadOnlyCollection<TaskThread>(this.taskThreads);
 58             }
 59         }
 60 
 61 
 62         #endregion
 63 
 64         #region Methods
 65 
 66         /// <summary>
 67         /// Initializes the task manager with the property values specified in the configuration file.
 68         /// 这里读取所有的计划任务,并对计划任务按照运行时间间隔分组,每组的任务创建一个TaskThread
 69         /// </summary>
 70         public void Initialize()
 71         {
 72             this.taskThreads.Clear();
 73 
 74             // 按运行时间间隔分组
 75             var scheduleTasks = ScheduleTaskList.GetAllScheduleTask().OrderBy(x => x.Seconds).ToList();
 76 
 77             // group by threads with the same seconds
 78             foreach (var scheduleTaskGrouped in scheduleTasks.GroupBy(x => x.Seconds))
 79             {
 80                 // create a thread
 81                 var taskThread = new TaskThread { Seconds = scheduleTaskGrouped.Key };
 82                 foreach (var scheduleTask in scheduleTaskGrouped)
 83                 {
 84                     var schedTask = new ScheduleTask
 85                                         {
 86                                             Type = scheduleTask.Type,
 87                                             StopOnError = scheduleTask.StopOnError,
 88                                             Seconds = scheduleTask.Seconds,
 89                                             Name = scheduleTask.Name,
 90                                             LastSuccess = scheduleTask.LastSuccess,
 91                                             LastStart = scheduleTask.LastStart,
 92                                             LastEnd = scheduleTask.LastEnd,
 93                                             Enabled = scheduleTask.Enabled,
 94                                         };
 95                     var task = new Task(schedTask);
 96                     taskThread.AddTask(task);
 97                 }
 98 
 99                 this.taskThreads.Add(taskThread);
100             }
101 
102             // sometimes a task period could be set to several hours (or even days).
103             // in this case a probability that it'll be run is quite small (an application could be restarted)
104             // we should manually run the tasks which weren't run for a long time
105             var notRunTasks = scheduleTasks
106                 
107                 // find tasks with "run period" more than 30 minutes
108                 .Where(x => x.Seconds >= NotRunTasksInterval)
109                 .Where(x => !x.LastStart.HasValue || x.LastStart.Value.AddSeconds(x.Seconds) < DateTime.Now)
110                 .ToList();
111 
112             // create a thread for the tasks which weren't run for a long time
113             if (notRunTasks.Count > 0)
114             {
115                 var taskThread = new TaskThread
116                                      {
117                                          RunOnlyOnce = true,
118                                          Seconds = 60 * 5
119                                          // let's run such tasks in 5 minutes after application start
120                                      };
121                 foreach (var scheduleTask in notRunTasks)
122                 {
123                     var schedTask = new ScheduleTask
124                                         {
125                                             Type = scheduleTask.Type,
126                                             StopOnError = scheduleTask.StopOnError,
127                                             Seconds = scheduleTask.Seconds,
128                                             Name = scheduleTask.Name,
129                                             LastSuccess = scheduleTask.LastSuccess,
130                                             LastStart = scheduleTask.LastStart,
131                                             LastEnd = scheduleTask.LastEnd,
132                                             Enabled = scheduleTask.Enabled,
133                                         };
134                     var task = new Task(schedTask);
135                     taskThread.AddTask(task);
136                 }
137 
138                 this.taskThreads.Add(taskThread);
139             }
140         }
141 
142         /// <summary>
143         /// Starts the task manager
144         /// </summary>
145         public void Start()
146         {
147             foreach (var taskThread in this.taskThreads)
148             {
149                 taskThread.InitTimer();
150             }
151         }
152 
153         /// <summary>
154         /// Stops the task manager
155         /// </summary>
156         public void Stop()
157         {
158             foreach (var taskThread in this.taskThreads)
159             {
160                 taskThread.Dispose();
161             }
162         }
163 
164         #endregion
165     }
TaskManager

说明:负责初始化所有的计划任务,并且对计划任务进行分组,提供任务计划的开始和停止,维护TaksThread列表

 

使用代码:

     TaskManager.Instance.Initialize();// 初始化任务

     TaskManager.Instance.Start();  // 开始任务

 

五、运行结果

3bc865b3-be89-4c92-ad7f-5d7e8ce925fa

根据上图可以看到,任务正常执行,并且是每隔30s记录一次日志,说明了上面的代码是正常的,也说明了采用定时器的方案,可以实现web下定义和执行计划任务的

 

六、总结

    这篇文章主要是提供了web下实现计划任务的一种解决思路和具体方案,不在需要写windows服务,通过自定义任务管理器和任务线程达到定时执行某项任务的功能。

    具体可以使用的场景有:异步发送邮件、异步发送短信等

       源码:下载地址:http://share.weiyun.com/d6140bfa1b3ce4e968f58c5335704551

       技术交流:351157970 (QQ)   59557329(QQ群:C#基地)

posted @ 2015-06-13 07:59  诸葛小亮  阅读(587)  评论(5)    收藏  举报