好,入正题.拿到一个系统,进行分析.先大概看一下它解决方案下的项目.该解决方案下有六个项目,基本很好理解.然后打开WEB项目下的配置文件!从中发现
<Event type = "Dottext.Search.SearchEngineSchedule, Dottext.Search" minutes = "1" key = "SearchEngine" />
<Event type = "Dottext.Framework.Tracking.StatsQueueSchedule, Dottext.Framework" minutes = "5" key = "StatsQueue" />
</Events>
有朋友可能会说,你为什么会先看到这个配置,因为很简单.我首先对他的事件调度很感兴趣,大致阅览配置文件,只有这一部分看上去非常像,另外再看到Event type就是在Dottext.Search项目下.可以确定搜索的索引文件生成.就是靠这个事件.
这里的Events并不是web.config本身的属性,往上看会发现这里的Events配置节点处于BlogConfigurationSettings配置节点下.这是一个自定义的配置节.到上面去找他的处理器
<!--声明了自定义配置节处理程序-->
<section name="BlogConfigurationSettings" type="Dottext.Framework.Util.XmlSerializerSectionHandler, Dottext.Framework" />
<section name="HandlerConfiguration" type="Dottext.Framework.Util.XmlSerializerSectionHandler, Dottext.Framework" />
<section name="SearchConfiguration" type="Dottext.Framework.Util.XmlSerializerSectionHandler, Dottext.Framework" />
<section name="microsoft.web.services" type="Microsoft.Web.Services.Configuration.WebServicesConfiguration, Microsoft.Web.Services, Version=2.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
<section name="codeHighlighter" type="ActiproSoftware.CodeHighlighter.CodeHighlighterConfigurationSectionHandler, ActiproSoftware.CodeHighlighter" />
</configSections>
发现BlogConfigurationSettings配置节是由自己写的Dottext.Framework.Util.XmlSerializerSectionHandler处理.这里有兴趣可以进去看看.
public class XmlSerializerSectionHandler : IConfigurationSectionHandler
{
public object Create(object parent, object configContext, System.Xml.XmlNode section)
{
XPathNavigator nav = section.CreateNavigator();
string typename = (string) nav.Evaluate("string(@type)");//是从当前XML(配置文件是一个符合xml要求的文档)节点处,获取”type”属性
Type t = Type.GetType(typename);//然后按照属性描述,获得一个.net的类型
/*这种生成类的方法是区别于new 方法生成具体类的另外途径,好处就是灵活根据具
* 体环境内容(甚至是用户交互输入的类型描述字符串)就可以生成获得托管类型。(此处反射细节请参考MSDN)。
* 坏处就是可能隐藏着类型错误,运行时出错,导致不可预料(好像这个词在windows编程时代相当的常见)例外甚至系统崩溃。
*/
//当然,上面仅仅创建了类型是不够用的,还需要通过一定途径来确定生成类的具体状态,
//这有用到OOP语言的重要特性—序列化,将一个对象存储器来,以及从存储中还原具体对象的机制。
//这里使用的是System提供的 XmlSerializer 类的反序列化方法Deserialize。反序列化后面还有很多
//代码涉及到,我认为现在就大致理解为“通过这个反序列化,我们刚刚得到的类实例中的属性、成员变
//量获得了赋值,进入到某个状态,就好像我们此处运行了new 语法和进行了对象的构造以及赋值”即可。更进一步的可以从权威资料MSDN获取。
XmlSerializer ser = new XmlSerializer(t);
return ser.Deserialize(new XmlNodeReader(section));
}
上面为方便一些朋友理解,我加上了注释!它的作用是将类的实例属性持久化到web.config.当程序运行时再从web.config文件取出属性还原类实例!当然,也方便修改!而外部调用是直接用系统提供的方法获取.如下
BlogConfigurationSettings bs = (BlogConfigurationSettings)System.Configuration.ConfigurationSettings.GetConfig("BlogConfigurationSettings");
//.NET2.0方式
BlogConfigurationSettings bs = (BlogConfigurationSettings)System.Configuration.ConfigurationManager.GetSection("BlogConfigurationSettings");
这样就可以从配置文件中获取到一个BlogConfigurationSettings实例!他的属性值均来自配置文件!这就是通常所说的序列化.给开发可以带来很大便利!但使用者也要清楚.序列化和反序列化是比较耗性能的!所以在使用时应该注意代码书写方式.否则用的不当将带来性能问题!
好了,我们现在去看Dottext.Search.SearchEngineSchedule.cs这个感兴趣的类文件
{
public SearchEngineSchedule()
{
//
// TODO: Add constructor logic here
//
}
#region IEvent Members
public void Execute(object state)
{
Log log = new Log();
log.Title = "Search Index";
log.Message = string.Format("Daily ({0}) Build",log.StartDate.ToShortDateString());
IndexManager.RebuildSafeIndex(30);
log.EndDate = DateTime.Now;
LogManager.Create(log);
}
#endregion
}
{
/// <summary>
/// Interface for defining an event.
/// </summary>
public interface IEvent
{
void Execute(object state);
}
}
这个接口很简单,就定义了一个Execute方法!再看看他同命名空间下有哪些其他文件!
1.Event.cs 事件实体 它里面主要定义事件的一些属性,如是否应该执行,最后执行时间,对应的执行方法的类等等.
2.EventHttpModule.cs
{
static Timer eventTimer;//这里很重要
public EventHttpModule()
{
//
// TODO: Add constructor logic here
//
}
#region IHttpModule Members
//Init方法是实现了IHttpModule中的方法,它在应用程序接受到请求时就会执行
public void Init(System.Web.HttpApplication application)
{
if (eventTimer == null)
{
/* 这里是关键 用了一个Timer来实现定时执行任务 初始访问 1分钟后 会调用ScheduledEventWorkCallback
* 方法.之后 每EventManager.TimerMinutesInterval分钟执行一次.具体看配置
* 这里有几点需要注意 eventTimer 变量不能定义在本方法内部,一定要是一个类中的静态变量
* 否则,当线程执行完本方法后,.NET GC垃圾回收机制,会随时将它回收,达不到应用效果.
* 将eventTimer定义为静态的并且在这里有一个引用.这样可以保证它一直是活动的
* 当然有情况会断掉,一是IIS服务器关闭了,二是IIS进程回收也会停止
*/
eventTimer = new Timer(new TimerCallback(ScheduledEventWorkCallback), application.Context, 60000, EventManager.TimerMinutesInterval * 60000);
}
}
private void ScheduledEventWorkCallback(object sender)
{
try
{
EventManager.Execute();
}
catch(Exception ex)
{
LogManager.CreateExceptionLog(ex,"Failed ScheduledEventCallBack");
}
}
public void Dispose()
{
eventTimer = null;
}
#endregion
}
<add name="UrlReWriteModule" type="Dottext.Common.UrlManager.UrlReWriteModule, Dottext.Common" />
<add name="EventHttpModule" type="Dottext.Framework.ScheduledEvents.EventHttpModule, Dottext.Framework" />
<!--<add name="MsftBlogsHttpModule" type= "AspNetWeb.MsftBlogsHttpModule, MsftBlogsHttpModule" />-->
</httpModules>
3.EventManager.cs
/// EventManager is called from the EventHttpModule (or another means of scheduling a Timer). Its sole purpose
/// is to iterate over an array of Events and deterimine of the Event's IEvent should be processed. All events are
/// added to the managed threadpool.
/// 它是任务的管理器,负责取出并调用线程池中线程来执行这些任务
/// </summary>
public class EventManager
{
private EventManager()
{
}
public static readonly int TimerMinutesInterval = 5;
public static void Execute()
{
//第一步 取出所有需要执行的任务
Event[] items = Config.Settings.ScheduledItems;//这里系统是从配置文件中读取到所有的Events子节点Event
Event item = null;
//第二步 将这些任务一个个放入线程池队列中等待执行
if(items != null)
{
for(int i = 0; i<items.Length; i++)
{
item = items[i];
if(item.ShouldExecute)
{
item.UpdateTime();
IEvent e = item.IEventInstance;//接口编程,将实例转成IEvent接口
//将任务放入线程池队列中等待执行 接口IEvent的Execute方法
ManagedThreadPool.QueueUserWorkItem(new WaitCallback(e.Execute));
}
}
}
}
}
关于ManagedThreadPool.cs 线程池的管理器!下一篇文章再写