您的位置:首页 > 编程语言 > ASP

ASP.net实现WEB站点的后台定时任务+示例

2009-08-13 14:38 651 查看
先转贴一下经典哈:

1、为什么WEB站点要用后台定时任务

也许有人要问,WEB站点本身就后台服务了,为什么还要定制后台定时任务呢?

其实很简单,做过实际WEB应用的人都知道。一个实际站点内经常要做一些后台处理,如统计、评价、数据更新等操作,其中一些任务是非常有规律地重复,当然可以由管理员定期去执行我们开发好的WEB页上的Click事件(大家知道,IIS是客户端响应才执行后台程序
的)。

另外还一种情况,就是有些Click执行的操作,IIS响应时间是比较长的,比如作者去年做的一个应用,是评价某一地区的XX问题,由于后台执行一个模型系统,Click后要等3分钟左右才出结果(哈哈,这样的等待你受得了吗?)。这时候就可以用后台定时任务来提前实现,将结果存放好或分解成响应快的任务。

2、如何实现

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

ASP.net 的后台处理技术 

在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完成定时器的初始化,模块的参数配置等功能。

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

一个实用ASP.Net 后台处理类

呵.这回跟大家讨论下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);

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

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

最后再添加个简单的调用示例:

static void Main(string[] args)

        {

            ExecItem ei = new ExecItem(typeof(CacheManager), "RefreshObject", new object[] { },

   "缓存刷新",

    "",

    false);  //注意以后可以设置为false,即刷新任务不保存到磁盘,以免影响磁盘性能.//Objtype, Params, ct

           

            BkExecer be = BkExecManager.Execer;

            be.AddBkExecItem(ei);

            string msg = string.Empty;

            be.DoBkExec((object)msg);

            //Console.ReadLine();      

        }

类CacheManager:

public class CacheManager

    {

        public CacheManager(){}

        public static void RefreshObject()

        {

            string filePath = Path.Combine(BkExecManager.DataPath, "出错logFile.txt");

            if (!File.Exists(filePath))

            {

                File.Create(filePath);

                System.Threading.Thread.Sleep(1000);

            }

            using (StreamWriter sw = new StreamWriter(filePath,true,Encoding.Default))

            {

                try

                {

                    sw.WriteLine("这是一个定时任务测试--{0}",DateTime.Now);

                }

                catch (Exception ex)

                {

                }

                finally

                {

                    sw.Flush();

                    sw.Close();

                }

            }

            //Console.WriteLine("This is a test!");

            //Console.ReadLine();

        }


}


 
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息