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

  • 相关阅读:
    hdoj2187:悼念512汶川大地震遇难同胞 (贪心)
    2.0其它之Transform详解,以及UIElement和FrameworkElement的常用属性
    2.0外观之样式, 模板, 视觉状态和视觉状态管理器
    2.0图形之Ellipse, Line, Path, Polygon, Polyline, Rectangle
    2.0控件之ListBox, MediaElement, MultiScaleImage, PasswordBox, ProgressBar, RadioButton
    2.0画笔之SolidColorBrush, ImageBrush, VideoBrush, LinearGradientBrush, RadialGradientBrush
    2.0图形之基类System.Windows.Shapes.Shape
    2.0交互之鼠标事件和键盘事件
    2.0控件之ScrollViewer, Slider, StackPanel, TabControl, TextBlock, TextBox, ToggleButton
    2.0交互之InkPresenter(涂鸦板)
  • 原文地址:https://www.cnblogs.com/yuanbao/p/883934.html
Copyright © 2011-2022 走看看