zoukankan      html  css  js  c++  java
  • 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 线程池的管理器!下一篇文章再写
  • 相关阅读:
    在浏览器地址栏按回车、F5、Ctrl+F5刷新网页的区别
    RESTful 的总结
    Mvc项目部署IIS报错:没有为请求的URL配置默认文档,并且没有在服务器设置目录浏览
    Ajax的请求方式几传参的区别
    响应式布局中的CSS相对量
    理解 ES6 语法中 yield* 关键字的作用
    理解 ES6 语法中 yield 关键字的返回值
    配置IIS Express以便通过IP地址访问调试的网站
    在IntelliJ IDEA 13中配置OpenCV的Java开发环境
    iOS UITableView获取cell的indexPath及cell内部按钮点击事件处理
  • 原文地址:https://www.cnblogs.com/eflylab/p/1416109.html
Copyright © 2011-2022 走看看