• Nop 源码分析四 任务系统


    分析TaskManager.Instance.Initialize();  下面是一个实体抽象父类,ScheduleTask 继承此类。

    namespace Nop.Core
    {
        /// <summary>
        /// Base class for entities
        /// </summary>
        public abstract partial class BaseEntity
        {
            /// <summary>
            /// Gets or sets the entity identifier
            /// </summary>
            public int Id { get; set; }
    
            public override bool Equals(object obj)
            {
                return Equals(obj as BaseEntity);
            }
    
            private static bool IsTransient(BaseEntity obj)
            {//default返回类型默认值,int是0,string是null等
                return obj != null && Equals(obj.Id, default(int));
            }
    
            private Type GetUnproxiedType()
            {
                return GetType();
            }
    
            public virtual bool Equals(BaseEntity other)
            {
                if (other == null)
                    return false;
    
                if (ReferenceEquals(this, other))
                    return true;
                //如果2个数都不是0并且相等
                if (!IsTransient(this) &&
                    !IsTransient(other) &&
                    Equals(Id, other.Id))
                {//如果两个类型相同或是继承关系  返回true.IsAssignableFrom:确定当前实例的类型是否可以从指定类型的实例分配
                    //http://javapub.iteye.com/blog/764668 isAssignableFrom是用来判断一个类Class1和另一个类Class2是否相同或是另一个类的超类或接口
                    var otherType = other.GetUnproxiedType();
                    var thisType = GetUnproxiedType();
                    return thisType.IsAssignableFrom(otherType) ||
                            otherType.IsAssignableFrom(thisType);
                }
    
                return false;
            }
    
            public override int GetHashCode()
            {
              //http://baike.baidu.com/link?url=qKWysQqQA09bgOa6JzQylv7PGB-jiPMfUjAFvOIFqxxK45Vjkft7J3zOcib5zJVxcN2ZH4NeOkWlyh-079Cgyq  百度百科哈希吗,
                //哈希算法不一样,解释:哈希码产生的依据:哈希码并不是完全唯一的,它是一种算法,让同一个类的对象按照自己不同的特征尽量的有不同的哈希码,
              //但不表示不同的对象哈希码完全不同。也有相同的情况,看程序员如何写哈希码的算法。   例如:OBJECT类型,由于内存地址不一样,所以哈希码也不一样。
              //String,根据字符串用特殊算法返回哈希码。 int  哈希码与数值相同。
                if (Equals(Id, default(int)))
                    return base.GetHashCode();
                return Id.GetHashCode();
            }
            //操作符重载
            public static bool operator ==(BaseEntity x, BaseEntity y)
            {
                return Equals(x, y);
            }
    
            public static bool operator !=(BaseEntity x, BaseEntity y)
            {
                return !(x == y);
            }
        }
    }

    下面是计划任务类,含有名称、运行时段Seconds 、type、是否可用等

    namespace Nop.Core.Domain.Tasks
    {
        public class ScheduleTask : BaseEntity
        {
            /// <summary>
            /// Gets or sets the name 类名应该是计划任务 Schedule计划、安排
            /// </summary>
            public string Name { get; set; }
    
            /// <summary>
            /// Gets or sets the run period (in seconds)
            /// </summary>
            public int Seconds { get; set; }
    
            /// <summary>
            /// Gets or sets the type of appropriate ITask class
            /// </summary>
            public string Type { get; set; }
    
            /// <summary>
            /// Gets or sets the value indicating whether a task is enabled
            /// </summary>
            public bool Enabled { get; set; }
    
            /// <summary>
            /// Gets or sets the value indicating whether a task should be stopped on some error
            /// </summary>
            public bool StopOnError { get; set; }
    
            public DateTime? LastStartUtc { get; set; }
    
            public DateTime? LastEndUtc { get; set; }
    
            public DateTime? LastSuccessUtc { get; set; }
        }
    }

    计划任务服务类,增删改查 计划任务等操作。

    namespace Nop.Services.Tasks
    {
        /// <summary>
        /// Task service interface
        /// </summary>
        public partial interface IScheduleTaskService
        {
            /// <summary>
            /// Deletes a task
            /// </summary>
            /// <param name="task">Task</param>
            void DeleteTask(ScheduleTask task);
    
            /// <summary>
            /// Gets a task
            /// </summary>
            /// <param name="taskId">Task identifier</param>
            /// <returns>Task</returns>
            ScheduleTask GetTaskById(int taskId);
    
            /// <summary>
            /// Gets a task by its type
            /// </summary>
            /// <param name="type">Task type</param>
            /// <returns>Task</returns>
            ScheduleTask GetTaskByType(string type);
    
            /// <summary>
            /// Gets all tasks
            /// </summary>
            /// <param name="showHidden">A value indicating whether to show hidden records</param>
            /// <returns>Tasks</returns>
            IList<ScheduleTask> GetAllTasks(bool showHidden = false);
    
            /// <summary>
            /// Inserts a task
            /// </summary>
            /// <param name="task">Task</param>
            void InsertTask(ScheduleTask task);
    
            /// <summary>
            /// Updates the task
            /// </summary>
            /// <param name="task">Task</param>
            void UpdateTask(ScheduleTask task);
        }

    下面是TaskManager.Instance.Initialize();  的代码

    /// <summary>
            /// Initializes the task manager with the property values specified in the configuration file.
            /// </summary>
            public void Initialize()
            {
                this._taskThreads.Clear();
    
                var taskService = EngineContext.Current.Resolve<IScheduleTaskService>();
                var scheduleTasks = taskService
                    .GetAllTasks()
                    .OrderBy(x => x.Seconds)
                    .ToList();
    
                //group by threads with the same seconds
                foreach (var scheduleTaskGrouped in scheduleTasks.GroupBy(x => x.Seconds))
                {
                    //create a thread
                    var taskThread = new TaskThread
                                         {
                                             Seconds = scheduleTaskGrouped.Key
                                         };
                    foreach (var scheduleTask in scheduleTaskGrouped)
                    {
                        var task = new Task(scheduleTask);
                        taskThread.AddTask(task);
                    }
                    this._taskThreads.Add(taskThread);
                }
    
                //sometimes a task period could be set to several hours (or even days).
                //in this case a probability that it'll be run is quite small (an application could be restarted)
                //we should manually run the tasks which weren't run for a long time
                var notRunTasks = scheduleTasks
                    .Where(x => x.Seconds >= _notRunTasksInterval)
                    .Where(x => !x.LastStartUtc.HasValue || x.LastStartUtc.Value.AddSeconds(_notRunTasksInterval) < DateTime.UtcNow)
                    .ToList();
                //create a thread for the tasks which weren't run for a long time
                if (notRunTasks.Count > 0)
                {
                    var taskThread = new TaskThread
                    {
                        RunOnlyOnce = true,
                        Seconds = 60 * 5 //let's run such tasks in 5 minutes after application start
                    };
                    foreach (var scheduleTask in notRunTasks)
                    {
                        var task = new Task(scheduleTask);
                        taskThread.AddTask(task);
                    }
                    this._taskThreads.Add(taskThread);
                }
            }

    第一句 this._taskThreads.Clear();   是清楚所有taskThreads, 下面是taskThreads的声明。

    private readonly List<TaskThread> _taskThreads = new List<TaskThread>();

    TaskThread的集合,其中TaskThread的声明如下:

    namespace Nop.Services.Tasks
    {
        /// <summary>
        /// Represents task thread
        /// </summary>
        public partial class TaskThread : IDisposable
        {
            private Timer _timer;
            private bool _disposed;
            private readonly Dictionary<string, Task> _tasks;
    
            internal TaskThread()
            {
                this._tasks = new Dictionary<string, Task>();
                this.Seconds = 10 * 60;
            }
    
            private void Run()
            {
                if (Seconds <= 0)
                    return;
    
                this.StartedUtc = DateTime.UtcNow;
                this.IsRunning = true;
                foreach (Task task in this._tasks.Values)
                {
                    task.Execute();
                }
                this.IsRunning = false;
            }
    
            private void TimerHandler(object state)
            {
                this._timer.Change(-1, -1);
                this.Run();
                if (this.RunOnlyOnce)
                {
                    this.Dispose();
                }
                else
                {
                    this._timer.Change(this.Interval, this.Interval);
                }
            }
    
            /// <summary>
            /// Disposes the instance
            /// </summary>
            public void Dispose()
            {
                if ((this._timer != null) && !this._disposed)
                {
                    lock (this)
                    {
                        this._timer.Dispose();
                        this._timer = null;
                        this._disposed = true;
                    }
                }
            }
    
            /// <summary>
            /// Inits a timer
            /// </summary>
            public void InitTimer()
            {
                if (this._timer == null)
                {
                    this._timer = new Timer(new TimerCallback(this.TimerHandler), null, this.Interval, this.Interval);
                }
            }
    
            /// <summary>
            /// Adds a task to the thread
            /// </summary>
            /// <param name="task">The task to be added</param>
            public void AddTask(Task task)
            {
                if (!this._tasks.ContainsKey(task.Name))
                {
                    this._tasks.Add(task.Name, task);
                }
            }
    
    
            /// <summary>
            /// Gets or sets the interval in seconds at which to run the tasks
            /// </summary>
            public int Seconds { get; set; }
    
            /// <summary>
            /// Get or sets a datetime when thread has been started
            /// </summary>
            public DateTime StartedUtc { get; private set; }
    
            /// <summary>
            /// Get or sets a value indicating whether thread is running
            /// </summary>
            public bool IsRunning { get; private set; }
    
            /// <summary>
            /// Get a list of tasks
            /// </summary>
            public IList<Task> Tasks
            {
                get
                {
                    var list = new List<Task>();
                    foreach (var task in this._tasks.Values)
                    {
                        list.Add(task);
                    }
                    return new ReadOnlyCollection<Task>(list);
                }
            }
    
            /// <summary>
            /// Gets the interval at which to run the tasks
            /// </summary>
            public int Interval
            {
                get
                {
                    return this.Seconds * 1000;
                }
            }
    
            /// <summary>
            /// Gets or sets a value indicating whether the thread whould be run only once (per appliction start)
            /// </summary>
            public bool RunOnlyOnce { get; set; }
        }
    }

    清除所有任务线程,后通过依赖注入获取所有的 IScheduleTaskService 计划任务服务类,IScheduleTaskService 通过GetAllTasks方法获得所有任务,根据Seconds进行排序 返回scheduleTasks枚举。

    对GetAllTasks根据Seconds进行分组(也就是以Seconds为KEY),并循环每一个分组,new TaskThread类 代码:(其他代码在上面可找到)

    var taskThread = new TaskThread
                                         {
                                             Seconds = scheduleTaskGrouped.Key
                                         };

    然后再循环组内的每一个计划任务, new 一个 TASK(构造参数:计划任务),最后将new 的任务都添加到taskThread(任务线程内)。然后将所有的任务线程添加到List中。

    后面代码的意思是,对运行时段大于30分钟的项目,如果启动时间为空,或者上次启动时间已超过30分钟,在5分钟后立即执行一次,仅执行一次。以后就根据时间上面taskThread的循环进行执行。

    global.asax.cs 的TaskManager.Instance.Start(); 应该是开始任务运行,其代码如下:

    /// <summary>
            /// Starts the task manager
            /// </summary>
            public void Start()
            {
                foreach (var taskThread in this._taskThreads)
                {
                    taskThread.InitTimer();
                }
            }

    循环所有的任务线程,调用InitTimer方法。代码如下:

    /// <summary>
            /// Inits a timer
            /// </summary>
            public void InitTimer()
            {
                if (this._timer == null)
                {
                    this._timer = new Timer(new TimerCallback(this.TimerHandler), null, this.Interval, this.Interval);
                }
            }

    System.Threading.Timer控件是一个用于非UI界面的小型定时器。 上一句就是对timer进行初始化 通过this.Interval(比如是10分钟,则10分钟后开始执行、每十分钟执行一次),TimerHandler是要执行的方法,代码如下:

    private void TimerHandler(object state)
            {
                this._timer.Change(-1, -1);
                this.Run();
                if (this.RunOnlyOnce)
                {
                    this.Dispose();
                }
                else
                {
                    this._timer.Change(this.Interval, this.Interval);
                }
            }

    this._timer.Change(-1, -1); 是重新设置执行周期,都设置为-1就是停止timer, 然后运行Run方法。 最后如果只执行一次 就调用dispose方法,停止运行,并清理。如果是否,则设置 timer恢复定时器运行。 dispose方法如下:

    /// <summary>
            /// Disposes the instance
            /// </summary>
            public void Dispose()
            {
                if ((this._timer != null) && !this._disposed)
                {
                    lock (this)
                    {
                        this._timer.Dispose();
                        this._timer = null;
                        this._disposed = true;
                    }
                }
            }

    上面很简单,不用解释了。然后看RUN方法  代码如下:

    private void Run()
            {
                if (Seconds <= 0)
                    return;
    
                this.StartedUtc = DateTime.UtcNow;
                this.IsRunning = true;
                foreach (Task task in this._tasks.Values)
                {
                    task.Execute();
                }
                this.IsRunning = false;
            }

    每隔一定时间运行的RUN方法。设置开始时间,设置是否正在运行为true,执行._tasks字典类型的所有任务的Execute方法。最后设置是否正在运行为false.

    重点落在了Task.Execute上。找到这个类,代码如下:

    namespace Nop.Services.Tasks
    {
        /// <summary>
        /// Task
        /// </summary>
        public partial class Task
        {
            /// <summary>
            /// Ctor for Task
            /// </summary>
            private Task()
            {
                this.Enabled = true;
            }
    
            /// <summary>
            /// Ctor for Task
            /// </summary>
            /// <param name="task">Task </param>
            public Task(ScheduleTask task)
            {
                this.Type = task.Type;
                this.Enabled = task.Enabled;
                this.StopOnError = task.StopOnError;
                this.Name = task.Name;
            }
    
            private ITask CreateTask(ILifetimeScope scope)
            {
                ITask task = null;
                if (this.Enabled)
                {
                    var type2 = System.Type.GetType(this.Type);
                    if (type2 != null)
                    {
                        object instance;
                        if (!EngineContext.Current.ContainerManager.TryResolve(type2, scope, out instance))
                        {
                            //not resolved
                            instance = EngineContext.Current.ContainerManager.ResolveUnregistered(type2, scope);
                        }
                        task = instance as ITask;
                    }
                }
                return task;
            }
            
            /// <summary>
            /// Executes the task
            /// </summary>
            /// <param name="throwException">A value indicating whether exception should be thrown if some error happens</param>
            /// <param name="dispose">A value indicating whether all instances hsould be disposed after task run</param>
            public void Execute(bool throwException = false, bool dispose = true)
            {
                this.IsRunning = true;
    
                //background tasks has an issue with Autofac
                //because scope is generated each time it's requested
                //that's why we get one single scope here
                //this way we can also dispose resources once a task is completed
                var scope = EngineContext.Current.ContainerManager.Scope();
                var scheduleTaskService = EngineContext.Current.ContainerManager.Resolve<IScheduleTaskService>("", scope);
                var scheduleTask = scheduleTaskService.GetTaskByType(this.Type);
    
                try
                {
                    var task = this.CreateTask(scope);
                    if (task != null)
                    {
                        this.LastStartUtc = DateTime.UtcNow;
                        if (scheduleTask != null)
                        {
                            //update appropriate datetime properties
                            scheduleTask.LastStartUtc = this.LastStartUtc;
                            scheduleTaskService.UpdateTask(scheduleTask);
                        }
    
                        //execute task
                        task.Execute();
                        this.LastEndUtc = this.LastSuccessUtc = DateTime.UtcNow;
                    }
                }
                catch (Exception exc)
                {
                    this.Enabled = !this.StopOnError;
                    this.LastEndUtc = DateTime.UtcNow;
    
                    //log error
                    var logger = EngineContext.Current.ContainerManager.Resolve<ILogger>("", scope);
                    logger.Error(string.Format("Error while running the '{0}' schedule task. {1}", this.Name, exc.Message), exc);
                    if (throwException)
                        throw;
                }
    
                if (scheduleTask != null)
                {
                    //update appropriate datetime properties
                    scheduleTask.LastEndUtc = this.LastEndUtc;
                    scheduleTask.LastSuccessUtc = this.LastSuccessUtc;
                    scheduleTaskService.UpdateTask(scheduleTask);
                }
    
                //dispose all resources
                if (dispose)
                {
                    scope.Dispose();
                }
    
                this.IsRunning = false;
            }
    
            /// <summary>
            /// A value indicating whether a task is running
            /// </summary>
            public bool IsRunning { get; private set; }
    
            /// <summary>
            /// Datetime of the last start
            /// </summary>
            public DateTime? LastStartUtc { get; private set; }
    
            /// <summary>
            /// Datetime of the last end
            /// </summary>
            public DateTime? LastEndUtc { get; private set; }
    
            /// <summary>
            /// Datetime of the last success
            /// </summary>
            public DateTime? LastSuccessUtc { get; private set; }
    
            /// <summary>
            /// A value indicating type of the task
            /// </summary>
            public string Type { get; private set; }
    
            /// <summary>
            /// A value indicating whether to stop task on error
            /// </summary>
            public bool StopOnError { get; private set; }
    
            /// <summary>
            /// Get the task name
            /// </summary>
            public string Name { get; private set; }
    
            /// <summary>
            /// A value indicating whether the task is enabled
            /// </summary>
            public bool Enabled { get; set; }
        }
    }

    可以看出Task是一个普通的类,没有任何集成,但他是一个部分类,这就要小心了,说不定别的地方也可以再写一个部分Task类,现在我们仅看这个。

    里面包含了一些属性,包括:Name,Enabled,Type,isRunning等等。我们分析Execute方法。

    开始获取依赖注入的 scope,应该是基于请求的(就不查了),然后根据当前SCOPE获得计划任务服务对象。用计划任务服务对象根据当前Task的类型获得计划任务(GetTaskByType)。

    调用var task = this.CreateTask(scope);方法 获得ITask对象。代码如下:

    private ITask CreateTask(ILifetimeScope scope)
            {
                ITask task = null;
                if (this.Enabled)
                {
                    var type2 = System.Type.GetType(this.Type);
                    if (type2 != null)
                    {
                        object instance;
                        if (!EngineContext.Current.ContainerManager.TryResolve(type2, scope, out instance))
                        {
                            //not resolved
                            instance = EngineContext.Current.ContainerManager.ResolveUnregistered(type2, scope);
                        }
                        task = instance as ITask;
                    }
                }
                return task;
            }

    就是根据this.Type获得当前类型的一个实例。然后就是执行task.Execute();方法  当然还要设置一些最后开始时间、最后结束时间=最后成功运行时间 等  。最后scope.Dispose();释放scope.

    下面是ITask接口的代码:

    public partial interface ITask
        {
            /// <summary>
            /// Execute task
            /// </summary>
            void Execute();
        }

    回到Global.asax.cs 后面代码:就是调用调用日志 输出应用启动。结束~!!!!

    //log application start
                if (databaseInstalled)
                {
                    try
                    {
                        //log
                        var logger = EngineContext.Current.Resolve<ILogger>();
                        logger.Information("Application started", null, null);
                    }
                    catch (Exception)
                    {
                        //don't throw new exception if occurs
                    }
                }
  • 相关阅读:
    4章 关键的“构建”决策
    [转]EAGAIN、EWOULDBLOCK、EINTR与非阻塞 长连接
    5章 软件构建中的设计
    网站收集,慢慢积攒
    常见的SQL优化建议
    eclipse 最全快捷键 分享快乐与便捷
    设置Activity全屏和无标题
    Anaroid WebView详解大全
    cordova2.2对于CordovaWebView的配置
    在Android的webview中定制js的alert,confirm和prompt对话框的方法
  • 原文地址:https://www.cnblogs.com/runit/p/4178335.html
Copyright © 2020-2023  润新知