dottext阅读之系统调度分析

    记得很早以前下载过DUDU的dottext.但当时由于看说明配置过于繁杂没弄.由于最近有一个博客系统的项目.又打开dottext研究了下,发现它里面的搜索是基于Lucene.Net.并且搜索文件的更新是增量式.因为自己在以前公司做过关于Lucene的项目.当时每次更新索引都是编写的CS结构程序重新生成,然后更新到服务器,非常麻烦.于是自己对于dottext系统中如何直接在WEB中对该模块进行调度很感兴趣.在网上搜索了下有没有前人对这一块进行过分析,发现baidu,google均只能找到很有限的资料.只好这二天空余时间就自己研究了下,在这里就我的理解分析下dottext系统中的调度模块!

     好,入正题.拿到一个系统,进行分析.先大概看一下它解决方案下的项目.该解决方案下有六个项目,基本很好理解.然后打开WEB项目下的配置文件!从中发现

<Events>
            
<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配置节点下.这是一个自定义的配置节.到上面去找他的处理器

<configSections>
    
<!--声明了自定义配置节处理程序-->
        
<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处理.这里有兴趣可以进去看看.

//自定义的配置节处理器必须实现IConfigurationSectionHandler接口
    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文件取出属性还原类实例!当然,也方便修改!而外部调用是直接用系统提供的方法获取.如下

//.NET1.1方式
BlogConfigurationSettings bs = (BlogConfigurationSettings)System.Configuration.ConfigurationSettings.GetConfig("BlogConfigurationSettings");
//.NET2.0方式
BlogConfigurationSettings bs = (BlogConfigurationSettings)System.Configuration.ConfigurationManager.GetSection("BlogConfigurationSettings");

这样就可以从配置文件中获取到一个BlogConfigurationSettings实例!他的属性值均来自配置文件!这就是通常所说的序列化.给开发可以带来很大便利!但使用者也要清楚.序列化和反序列化是比较耗性能的!所以在使用时应该注意代码书写方式.否则用的不当将带来性能问题!
     好了,我们现在去看Dottext.Search.SearchEngineSchedule.cs这个感兴趣的类文件

public class SearchEngineSchedule : Dottext.Framework.ScheduledEvents.IEvent
    {
        
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
    }
我们发现这个类实现了Dottext.Framework.ScheduledEvents.IEvent这样一个接口.而其中只有一个Execute方法.我们初步猜这个方法就是具体调度要执行的任务代码.它里面有一些日志的记录以及具体的更新索引文件!我们先不关心该方法的体.现在想知道他如何实现调度的!去看看IEvent接口!
namespace Dottext.Framework.ScheduledEvents
{
    
/// <summary>
    
/// Interface for defining an event.
    
/// </summary>
    public interface IEvent
    {
        
void Execute(object state);
    }
}

这个接口很简单,就定义了一个Execute方法!再看看他同命名空间下有哪些其他文件!
1.Event.cs 事件实体 它里面主要定义事件的一些属性,如是否应该执行,最后执行时间,对应的执行方法的类等等.

Event.cs


2.EventHttpModule.cs

public class EventHttpModule : System.Web.IHttpModule
    {
        
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
    }
上面我注释的比较详细.ScheduledEventWorkCallback方法主要调用了EventManager.Execute()方法来执行任务.具体的什么任务在EventHttpModule不关心.这也不属于它的职责!另外需要本类正常运行是需要在web.config中配置一下httpModules
<httpModules>
            
<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>
上面的EventHttpModule是配置本类的!这样在系统接受到请求时会进过EventHttpModule.在里面触发timer.以后就会定期的执行!
3.EventManager.cs
/// <summary>
    
/// 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));
                    }
                }
            }
        }
    }
EventManager的现职很明确并单一.就是负责调出任务并放入线程池等待执行!
关于ManagedThreadPool.cs 线程池的管理器!下一篇文章再写

posted on 2009-03-18 23:59  冯岩  阅读(781)  评论(0编辑  收藏  举报

导航