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

利用缓存过期在ASP.NET中实现定时器

2016-08-17 08:57 375 查看


在B/S结构中要实现定时器(或者说是一个事务)实在不是一件好办的事。可当你在网上搜索“ASP.NET定时器”的时候,你会发现搜索结果是如此的多,可这大多数结果中的代码健壮性都是那样的脆弱——没有考虑诸如IIS进程的自然消亡IIS进程的故障崩溃重启服务器等等因素。这些不可捕捉的错误会让你的定时器失去定时的功能。可能会有人问,为什么要用Web程序做计时器呢?自己添加一个Windows服务或者在数据库中添加一个作业不就解决了么?可事实上,又有多少人对自己购买的虚拟主机具有如此权限呢?如果你用的是自己的服务器或者更高权限的服务器,那此文对你的确是多余了。

我曾经为ASP.NET中的Application_End感到窃喜,以为有了它,以上提及的自然消亡、故障崩溃都可迎刃而解了,可经过自己的实践发现那个Application_End真的是很无助——你在该代码段真的是nothing to do。国内搜索到的所谓ASP.NET定时器几乎都是采用System.Thread进程监控实现的,要让Thread在IIS进程中保持长期(比如1年365天时时刻刻)的稳定性实在是不敢打包票。

最后我在CodeProject上找到一篇文章《Simulate a Windows Service
using ASP.NET to run scheduled jobs》(在ASP.NET中模拟Widnows服务执行计划任务)。在这篇文章中,他利用缓存的过期来实现定时器功能。在ASP.NET中,当你添加缓存的时候,会有一个缓存回调函数让你同时加入,表示当缓存过期被系统清除的时候,你可以在这个回调函数中做一些事情。缓存回调函数正是实现ASP.NET定时器的重要基石。假设我们在00:00:00时刻加入一个缓存对象,缓存的过期时间我们设置为00:02:00,当到达00:02:00或者更提前的时候,缓存将被系统回收,当系统通知缓存被回收的时候我们再次添加一个缓存,如此循环往复(缓存被添加->过期->再次添加->过期……)从而模拟出了钟表秒针。当每一次缓存过期的时候我们都检查是否有需要执行的事务,有则执行,然后再次添加一个缓存,从而模拟出了定时器。

但是要想添加缓存,必须有用户请求Web服务器才能使用Cache。所以,在缓存过期的时候,除了检查是否有事务需要执行外,还要用WebClient(或者其他方法)请求我们的Web页面,在页面被请求的时候添加缓存。

上面的思路已经基本模拟出了定时器的功能,但对于服务器重启、IIS故障仍然没有解决。《Simulate a Windows Service using ASP.NET to run scheduled jobs》中提到,可以把Google Robots抓取你网页的频率设置大一点,当出现上述情况时,以尽快的速度产生Web请求。只要有人(或者机器人)在访问你的Web程序,缓存定时器(我们把它称为“缓存时钟”吧)就可以正常工作,而他的工作是由系统自行维护的,而不是Thread进程维护,所以稳定性会有很大的提高。经过我的实践,只要服务器没有重启、IIS进程没有崩溃,上述中采用在缓冲过期时请求Web页面(WebClient)的行为是不会让IIS进程自然消亡的(缓存过期时间长短、服务器配置也会影响)。(下划线文字的描述与事实不符,观测的结果可能受到网络蜘蛛的影响)具体如何实现,请看下文。

 
复制代码

using System;
using System.Data;
using System.Net;
using System.Configuration;
using System.Web;
using System.Text;
using System.Web.Caching;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;

public classCacheTimer

{

const string logPath = @"d:\cacheLogs\cacheLog.txt";
const string cacheKeyName = "myCacheTimer";//缓存的名字

const string rawUrl = "http://localhost/CacheTimer/CacheTimmer.aspx";//缓存过期时请求的页面地址

public void RegistCacheTimmer()

{

if (HttpContext.Current.Cache[cacheKeyName] != null) return;
try
{

HttpContext.Current.Cache.Add(cacheKeyName, '1', null,
DateTime.Now.AddSeconds(12), Cache.NoSlidingExpiration,
CacheItemPriority.High,
new CacheItemRemovedCallback(CacheItemOnRemoved));
WriteCacheLog("缓存时钟注册成功");
}
catch (Exception e)
{
WriteCacheLog("缓存时钟注册失败:" + e.Message);
}
}
protected void CacheItemOnRemoved(string key, object value, CacheItemRemovedReason reason)
{
if (key == cacheKeyName)
{
ExeJob();
RequestWebPage();
}
}

protected void ExeJob()
{

WriteCacheLog("被执行了");
//如果日期的天数能被7整除、下午13点到13点15分的时候,则执行

if (DateTime.Now.Day % 7 == 0 && DateTime.Now.Hour == 13 && DateTime.Now.Minute <= 15)

{
//执行数据操作或者其他任务

}
}
protected  void RequestWebPage()
{
WebClient wc = new WebClient();
wc.DownloadData(rawUrl);
}

public static void WriteCacheLog(string logInfor)
{
try
{
System.IO.StreamWriter sw = new System.IO.StreamWriter(logPath, true, Encoding.GetEncoding("GB2312"));
sw.WriteLine(DateTime.Now.ToString() + "   " + logInfor);
sw.Close();
sw.Dispose();
}
catch
{

//调试的时候注意是否对该文件有写权限

}
}
}


 
添加缓存:RegistCacheTimmer
请求页面:RequestWebPage
执行任务:ExeJob
缓存过期时候执行请求页、执行任务:CacheItemOnRemoved
写日志的方法:WriteCacheLog

然后在Global.asax文件中添加下列代
4000
码段:

 
复制代码
void Application_BeginRequest(Object sender, EventArgs e)
{
CacheTimer registTimer = new CacheTimer();
registTimer.RegistCacheTimmer();
}


 
但是如果我们的任务在没有执行完毕,IIS进程崩溃或者服务器重启了,怎么办?我的建议是把任务写人文件(包括数据库),检查的时候直接检查任务文件就可以保证重启IIS进程,任务不丢失了。

 

感谢《Simulate a Windows Service using ASP.NET to run scheduled jobs》的作者提供此定时器的解决方法。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  缓存 定时