zoukankan      html  css  js  c++  java
  • 如何实现WEB站点的后台定时任务?

    1、为什么WEB站点要用后台定时任务
            也许有人要问,WEB站点本身就后台服务了,为什么还要定制后台定时任务呢?
            其实很简单,做过实际WEB应用的人都知道。一个实际站点内经常要做一些后台处理,如统计、评价、数据更新等操作,其中一些任务是非常有规律地重复,当然可以由管理员定期去执行我们开发好的WEB页上的Click事件(大家知道,IIS是客户端响应才执行后台程序的)。
            另外还一种情况,就是有些Click执行的操作,IIS响应时间是比较长的,比如作者去年做的一个应用,是评价某一地区的XX问题,由于后台执行一个模型系统,Click后要等3分钟左右才出结果(哈哈,这样的等待你受得了吗?)。这时候就可以用后台定时任务来提前实现,将结果存放好或分解成响应快的任务。

    2、如何实现
            作者首先想到的是定时器,在自己开发之前,按照以前的习惯上网先查前人的成果,没想到,还真有人想过了,也做过了。在这就不再分析了,以下是摘录原文:

    在ASP.net的WEB开发中经常会碰到这样的一个问题:即用户操作响应慢的情况。

    出现这种情况的原因可能是本身用户操作就是一个耗时的操作,一般。Net程序设计时均在用户提交操作时即在后台处理,等到处理完成后再将操作结果返回给用户。 在小一点的系统中这样设计简单易行,而且性能上也不会有多大的问题,但在大一点的系统中这种设计就会给用户带来不好的操作体验,影响用户对系统的印象好坏。

    在我以前实施的系统中对这种情况一般有两种处理方式:一、将用户操作直接记录到后台数据库,由后台程序定时扫描来执行。二、采用Asp.net的定时处理方式,直接在WEB服务器层来进行处理。

    两种方式差别并不大,第一种方式主要需要一个后台程序来完成这个扫描工作。

    我在这里简单介绍下第二种方式。

    它的核心处理就是System.Threading.Timer。这个定时类可以用于在后台定时执行用户提交操作,

    它的使用方法:

    System.Threading.TimerCallback t=new System.Threading.TimerCallback (你的处理方法);

    System.Threading.Timer t = new System.Threading.Timer(t,null,1000,5000);

    这一段说明是在启动1秒后每隔5秒就调用所指定的代理。

    在具体实现时我定义了三个类。

    1、BkExecItem用于保存用户提交操作,同时它也可以序列化到磁盘上,以免关键后台任务丢失。

    2、BkExec用于执行。它通过反射来调用BkExecItem中所指定的方法。另外它中间还维护一个先入

    先出队列Queue<BkExecItem>,这个队列记录全部的后台处理项。

    3、BkManager完成定时器的初始化,模块的参数配置等功能。

    呵,暂时总结到这里。下次我会将代码也贴上来,供大家参考下。

    Trackback: http://tb.blog.csdn.net/TrackBack.aspx?PostId=1681816


    呵.这回跟大家讨论下ASP.net 后台处理 ,并会把我们当前项目中应用的一个后台处理类的代码贴上来参考.

    后台处理也是现在管理系统设计中需要考虑到的一个问题.

    什么是后台处理,可以简单认为不是在用户进程处理中完成用户提交的操作,而是将这一处理放到服务端后台进程来处理.

    加入后台处理后,可以提高前台用户的操作速度,改善用户操作体验.

    对于一般用户来说他对于一个系统的基本要求就是响应及时,用户很难对一个提交操作后需要等待10秒以后的管理系统产生好感,但在实际系统运行中用户操作是很难在短时间内得到响应,所以这个时候后台处理就可以发挥作用了.

    我在后面所帖代码中,将需要后台处理的任务均定义成一个ExecItem对象.用户提交操作后,系统将就操作转成一个ExecItem对象加入到BkExecManager(后台处理管理对象)中的一个先入先出的队列中.

    网站在启动时会自动启动BkExecManager,而BkExecManager则启动一个定时器来定时处理后台任务队列.

    在处理完成时BkExecManager就队列中移去任务对象,如果操作失败将以邮件方式通知管理员来完成问题处理.

    呵.现在贴代码!

    1,后台处理管理对象
    public class BkExecManager
        {  //定时回调。
            private static TimerCallback timerDelegate;
            private static Timer stateTimer;
            private static BkExecer m_Execer;
            public static string DataPath;
            public static string BkManager = "XXXX";
            public static int BkBufSize = 100;

            private static int Interval = 10000;

            public static BkExecer Execer
            {
                get { return m_Execer; }
            }

            static BkExecManager()
            {
                DataPath = System.AppDomain.CurrentDomain.BaseDirectory + "BkItem\\";

                if (System.Configuration.ConfigurationManager.AppSettings["Interval"] != null)
                    Interval = Convert.ToInt32(System.Configuration.ConfigurationManager.AppSettings["Interval"]);
                if (System.Configuration.ConfigurationManager.AppSettings["BkBufSize"] != null)
                    BkBufSize = Convert.ToInt32(System.Configuration.ConfigurationManager.AppSettings["BkBufSize"]);
                if (System.Configuration.ConfigurationManager.AppSettings["BkManager"] != null)
                    BkManager = System.Configuration.ConfigurationManager.AppSettings["BkManager"];
      

                m_Execer = new BkExecer();
               
                //初始化回调
                timerDelegate = new TimerCallback(m_Execer.DoBkExec);

                //初始化定时器
                stateTimer = new Timer(timerDelegate, null, 5000, Interval);

            }

            /// <summary>
            /// 停止定时器.
            /// </summary>
            static void BkExecQuit()
            {
                stateTimer.Dispose();
            }
        }


    2,后台处理执行
    public class BkExecer
        {
            //维护一
    个前进前出的队列。
            private Queue<ExecItem> m_BkExecItemList;
            private static object lockHelper = new object();
            private static bool m_IsBusy = false;
            public static bool IsBusy
            {
                get
                {
                    return m_IsBusy;
                }
            }

            public BkExecer()
            {
                m_BkExecItemList = new Queue<ExecItem>(BkExecManager.BkBufSize);

                ////读入待处理事项
                InitData();
            }

            private void InitData()
            {
                lock (lockHelper)
                {
                    string[] fnl = Directory.GetFiles(BkExecManager.DataPath);
                    foreach (string s in fnl)
                    {
                        if (!s.Contains(BKExecItemState.出错.ToString()))
                        {
                            ExecItem ei = ExecItem.GetObject(s);
                            m_BkExecItemList.Enqueue(ei);
                        }

                    }
                }
            }

            public void AddBkExecItem(ExecItem ei)
            {
                lock (lockHelper)
                {
                    //锁定资源。
                    m_BkExecItemList.Enqueue(ei);
                }
            }

            public void DoBkExec(object Msg)
            {
                ExecItem ei;


                while (m_BkExecItemList.Count > 0)
                {
                    lock (lockHelper)
                    {
                        ei = m_BkExecItemList.Dequeue();
                    }

                    int rv = -1;

                    try
                    {
                        BindingFlags flags = BindingFlags.InvokeMethod | BindingFlags.Instance |
                            BindingFlags.Public | BindingFlags.Static;

                        object t = ei.ExecItemClass.InvokeMember(ei.ExecItemMethed, flags, null, null, ei.ExecItemParamList);
                        if (t != null)
                            rv = Convert.ToInt32(t);
                        else
                            rv = 0;     //如果是无返回则直接设置零.
                    }
                    catch (Exception e)
                    {
                        //更新Ei的状态,保存到磁盘。
                        ei.FinishBkExec(false, e.Message);
                    }
                    finally
                    {
                        //更新Ei的状态,删除存件
                        //保存到磁盘。
                        if (rv >= 0)
                            ei.FinishBkExec(true, "");
                        else
                            ei.FinishBkExec(false, rv.ToString());
                    }
                }  
            }
        }


    3,任务对象

    public enum BKExecItemState { 待执行, 完成, 出错 };

        [Serializable]
        /// <summary>
        /// 后台命令集合
        /// 直接将这些后台命令二进制序列化到WEb服务器上保存。
        /// 如果完成后则从Web服务器上删除。
        /// 如果异常则发邮件通知管理员。
        /// </summary>
        public class ExecItem
        {
            /// <summary>
            /// 磁盘文档名称 。
            /// </summary>
            private string BKStoreFileName = "";

            private string ErrMsg = "";


            private BKExecItemState m_ItemState;

            public BKExecItemState ItemState
            {
                get { return m_ItemState; }
            }

            private DateTime m_ExecItemExecTime;

            public DateTime ExecItemExecTime
            {
                get { return m_ExecItemExecTime; }

            }

            private DateTime m_ExecItemCreateTime;

            public DateTime ExecItemCreateTime
            {
                get { return m_ExecItemCreateTime; }

            }
            private string m_ExecItemName;

            public string ExecItemName
            {
                get { return m_ExecItemName; }

            }
            private Type m_ExecItemClass;

            public Type ExecItemClass
            {
                get { return m_ExecItemClass; }

            }
            private string m_ExecItemMethed;

            public string ExecItemMethed
            {
                get { return m_ExecItemMethed; }

            }
            private object[] m_ExecItemParamList;

            public object[] ExecItemParamList
            {
                get { return m_ExecItemParamList; }

            }

            private string m_Op;

            /// <summary>
            /// 后台任务对象
            /// </summary>
            /// <param name="objtype">对象类型</param>
            /// <param name="ExecMethod">调用方法</param>
            /// <param name="param">调用参数</param>
            /// <param name="ExecName">任务名</param>
            /// <param name="Op">提交人</param>
            /// <param name="SavetoDisk">是否保存到磁盘</param>
            public ExecItem(Type objtype, string ExecMethod, object [] param, string ExecName, string Op,bool SavetoDisk)
            {
                this.BKStoreFileName = String.Format("{0} {1} {2}.bin", DateTime.Now.ToString("yyyy-MM-dd HH-mm-ss"), ExecMethod, Op);
                this.m_ExecItemClass = objtype;
                this.m_ExecItemCreateTime = DateTime.Now;
                this.m_ExecItemExecTime = DateTime.Now;
                this.m_ExecItemMethed = ExecMethod;
                this.m_ExecItemName = ExecName;
                this.m_ExecItemParamList = param;
                this.m_ItemState = BKExecItemState.待执行;
                this.m_Op = Op;

                if (SavetoDisk)
                SaveToDisk();
            }

            private void SaveToDisk()
            {
                IFormatter formatter = new BinaryFormatter();
                Stream stream = new FileStream(BkExecManager.DataPath + BKStoreFileName, FileMode.Create, FileAccess.Write, FileShare.None);
                formatter.Serialize(stream, this);
                stream.Close();
            }

            private void SaveToDisk2()
            {
                //
                string basedir = System.AppDomain.CurrentDomain.BaseDirectory;
                this.BKStoreFileName = String.Format("{0} {1} {2} {3}.bin", m_ExecItemCreateTime.ToString("yyyy-MM-dd HH-mm-ss"), this.m_ExecItemMethed, m_Op, m_ItemState.ToString ());

                IFormatter formatter = new BinaryFormatter();

                Stream stream = new FileStream(BkExecManager.DataPath + BKStoreFileName, FileMode.Create, FileAccess.Write, FileShare.None);
                formatter.Serialize(stream, this);
                stream.Close();
            }

            public static ExecItem GetObject(string s)
            {
                IFormatter formatter = new BinaryFormatter();

                Stream stream = new FileStream(s, FileMode.Open, FileAccess.Read, FileShare.None);
                ExecItem e = (ExecItem)formatter.Deserialize(stream);
                stream.Close();
                return e;
            }

            public void FinishBkExec(bool DoneOk, string Msg)
            {
                string FileName = BkExecManager.DataPath + BKStoreFileName;
                m_ExecItemExecTime = DateTime.Now;

                if (File.Exists(FileName))
                    File.Delete(FileName);

                if (!DoneOk)
                {
                    m_ItemState = BKExecItemState.出错;
                    ErrMsg = Msg;
                    SaveToDisk2();
                    MakeMail();
                }
            }

            private void MakeMail()
            {
                StringBuilder sb = new StringBuilder();
                sb.Append("提交人:").Append(this.m_Op).Append("<BR>");
                sb.Append("提交时间:").Append(this.ExecItemCreateTime).Append("<BR>");
                sb.Append("对象:").Append(this.m_ExecItemClass.Name).Append("<BR>");
                sb.Append("方法:").Append(this.m_ExecItemMethed).Append("<BR>");
                sb.Append("参数:");
                foreach (object o in this.m_ExecItemParamList)
                    sb.Append(o.ToString()).Append(",");
                sb.Append("<BR>");
                sb.Append("执行时间:").Append(this.m_ExecItemExecTime).Append("<BR>");
                sb.Append("错误信息:").Append(this.ErrMsg).Append("<BR>");
                string mb = sb.ToString();

                //APP.Mail.Send(m_Op + ":" + m_ExecItemClass.Name + "后台处理错", mb, "", BkExecManager.BkManager, "");
            }
        }


    具体调用方法为
    1,首先新调一个后台任务对象.
    2,将之加入到任务队列中.

         ExecItem ei = new ExecItem(typeof(CacheManager), "RefreshObject", new object[] { Objtype, Params, ct }, "缓存刷新", "", false);  //注意以后可以设置为false,即刷新任务不保存到磁盘,以免影响磁盘性能.
                BkExecManager.Execer.AddBkExecItem(ei);


    现在这个对象在我们项目中运行良好.

    后期还想继续完善这个对象.它现在的不足有 :没有让用户知道他提交的操作执行进度,操作结果等.

    Trackback: http://tb.blog.csdn.net/TrackBack.aspx?PostId=1768124

  • 相关阅读:
    前端JavaScript(2) --常用内置对象,函数,伪数组 arguments,关于DOM的事件操作,DOM介绍
    前端JavaScript(3)-关于DOM操作的相关案例,JS中的面向对象、定时器、BOM、位置信息
    前端JavaScript(1) --Javascript简介,第一个JavaScript代码,数据类型,运算符,数据类型转换,流程控制,百度换肤,显示隐藏
    前端CSS(3)
    CSS标签大全
    前端CSS(2)
    前端CSS(1)
    前端HTML(二/三)
    前端基础(1)
    第一个自定义HTML网页
  • 原文地址:https://www.cnblogs.com/yuanbao/p/883934.html
Copyright © 2011-2022 走看看