惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

小众软件
小众软件
A
About on SuperTechFans
博客园 - Franky
Engineering at Meta
Engineering at Meta
Recent Announcements
Recent Announcements
云风的 BLOG
云风的 BLOG
B
Blog
Microsoft Security Blog
Microsoft Security Blog
L
LangChain Blog
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
U
Unit 42
Martin Fowler
Martin Fowler
Y
Y Combinator Blog
Stack Overflow Blog
Stack Overflow Blog
博客园 - 叶小钗
Vercel News
Vercel News
Apple Machine Learning Research
Apple Machine Learning Research
The Cloudflare Blog
Last Week in AI
Last Week in AI
腾讯CDC
Microsoft Azure Blog
Microsoft Azure Blog
爱范儿
爱范儿
V
V2EX
G
Google Developers Blog

博客园 - 冯岩

RSA算法 Android JAVA C#互通 jquery.autocomplete.js 插件的自定义搜索规则 LINQ TO SQL 动态查询 ASPX多服务器控件下使用Jquery.validate.js 接受来自服务器的数据连接时发生超时(30000 毫秒)问题原因及解决方法 防止网页被客户端IE缓存 Javascript对日期的操作 C#读取被进程占用的文件 SqlServer 无日志文件附加 windows2003远程桌面退出后系统自动注销的解决方法 JS获取触发事件元素的方法 JS处理选取值 Marquee首尾相连不间断移动 开始完全显示 c#将数据导入Excel另类方法 windows Server 2008常见问题及解决方法 .NET 特性Attribute[三] .NET 特性Attribute[二] .NET 特性Attribute[一] [安装程序配置服务器失败]解决SQL Server2000安装失败
dottext阅读之系统调度分析
冯岩 · 2009-03-18 · via 博客园 - 冯岩

    记得很早以前下载过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 Memberspublic 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 线程池的管理器!下一篇文章再写