nopcommerce计划任务分析

对比了一下nopcommerce和orchard的计划任务,orchard的复杂的不是一点点,如果想拆下来自己用难度很大,搜索拆了orchard的lucene处理模块,邮件队列拆的discuznt和nopcommerce的结合,计划任务就拆nopcommerce的了,discuznt计划任务设计的没nopcommerce的好。

1.nopcommerce的tasks结构如下:

IScheduleTaskService.cs 接口,这个主要是获取数据库里的任务信息,ScheduleTaskService.cs去实现它就可以了,当然需要在容器里注入一下。

ITask 这个接口比较特别但是很重要,所有的任务处理类都要实现里面唯一的Execute方法。执行计划任务时就需要通过反射来执行这个实现。

namespace Nop.Services.Tasks
{
    /// <summary>
    /// Interface that should be implemented by each task
    /// </summary>
    public partial interface ITask
    {
        /// <summary>
        /// Execute task
        /// </summary>
        void Execute();
    }
}

核心类之一:Task.cs,这个主要是处理任务的执行过程及执行过程类的结果处理。

private ITask CreateTask()
        {
            ITask task = null;
            if (this.Enabled)
            {
                var type2 = System.Type.GetType(this._type);
                if (type2 != null)
                {
                    task = Activator.CreateInstance(type2) as ITask;
                }
                //this._enabled = task != null;
            }
            return task;
        }

通过反射来找到编写的计划任务类。例如下面的发送邮件的任务。

View Code
using System;
using Nop.Core.Infrastructure;
using Nop.Services.Logging;
using Nop.Services.Tasks;

namespace Nop.Services.Messages
{
    /// <summary>
    /// Represents a task for sending queued message 
    /// </summary>
    public partial class QueuedMessagesSendTask : ITask
    {

        /// <summary>
        /// Executes a task
        /// </summary>
        public void Execute()
        {
            var queuedEmailService = EngineContext.Current.Resolve<IQueuedEmailService>();
            var emailSender = EngineContext.Current.Resolve<IEmailSender>();

            var maxTries = 3;
            var queuedEmails = queuedEmailService.SearchEmails(null, null, null, null,
                true, maxTries, false, 0, 10000);
            foreach (var queuedEmail in queuedEmails)
            {
                var bcc = String.IsNullOrWhiteSpace(queuedEmail.Bcc) 
                            ? null 
                            : queuedEmail.Bcc.Split(new char[] { ';' }, StringSplitOptions.RemoveEmptyEntries);
                var cc = String.IsNullOrWhiteSpace(queuedEmail.CC) 
                            ? null 
                            : queuedEmail.CC.Split(new char[] { ';' }, StringSplitOptions.RemoveEmptyEntries);

                try
                {
                    emailSender.SendEmail(queuedEmail.EmailAccount, queuedEmail.Subject, queuedEmail.Body,
                       queuedEmail.From, queuedEmail.FromName, queuedEmail.To, queuedEmail.ToName, bcc, cc);

                    queuedEmail.SentOnUtc = DateTime.UtcNow;
                }
                catch (Exception exc)
                {
                    var logger = EngineContext.Current.Resolve<ILogger>();
                    logger.Error(string.Format("Error sending e-mail. {0}", exc.Message), exc);
                }
                finally
                {
                    queuedEmail.SentTries = queuedEmail.SentTries + 1;
                    queuedEmailService.UpdateQueuedEmail(queuedEmail);
                }
            }
        }
    }
}

执行完任务后需要将数据库里的任务记录状态更改,主要是时间状态变更。

核心执行方法:

        /// <summary>
        /// 执行任务
        /// </summary>
        public void Execute()
        {
            this._isRunning = true;
            try
            {
                var task = this.CreateTask();
                if (task != null)
                {
                    this._lastStartUtc = DateTime.UtcNow;
                    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.Resolve<ILogger>();
                logger.Error(string.Format("Error while running the '{0}' schedule task. {1}", this._name, exc.Message), exc);
            }
            
            try
            {
                //find current schedule task
                var scheduleTaskService = EngineContext.Current.Resolve<IScheduleTaskService>();
                var scheduleTask = scheduleTaskService.GetTaskByType(this._type);
                if (scheduleTask != null)
                {
                    scheduleTask.LastStartUtc = this.LastStartUtc;
                    scheduleTask.LastEndUtc = this.LastEndUtc;
                    scheduleTask.LastSuccessUtc = this.LastSuccessUtc;
                    scheduleTaskService.UpdateTask(scheduleTask);
                }
            }
            catch (Exception exc)
            {
                Debug.WriteLine(string.Format("Error saving schedule task datetimes. Exception: {0}", exc));
            }
            this._isRunning = false;
        }

 任务管理类:TaskManager.cs,主要负责任务的初始化,添加到线程列表,任务的开始和停止。需要在Global里初始化和开始任务,它会根据线程里的定时器自动读取任务列表执行任务。

            //开始执行任务
            if (databaseInstalled)
            {
                TaskManager.Instance.Initialize();
                TaskManager.Instance.Start();
            }

任务线程管理类:TaskThread.cs,任务线程类,TaskManager将任务都添加到此线程管理类里,此线程管理主要负责判断任务的执行状态,线程执行间隔时间及调用任务执行的主方法Execute,通过Timer定时器实现定时自动运行。

主方法为:

        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;
        }

从任务列表中读取任务并执行。

以上是简单的分析,目前只是拿来主义,在学习和整理的同时加深一下对开源代码的理解。

独立博客地址:http://www.jqpress.com/post/190.aspx

原文地址:https://www.cnblogs.com/jqbird/p/2554105.html