Community Server专题七: Job & Timer [转]

在CSHttpModule.cs文件中的Init方法下有这样一行:
None.gif
None.gif
None.gif接着在Dispose方法中还有这么一行:
None.gif
None.gif
None.gifJob ? 什么是Job,在CS运行过程中有什么用途,又是如何运行的?这篇专题将叙述Job的工作流程.
None.gif
None.gif你可以这里理解CS中的Job:“干一些零碎事情的钟点工”。
None.gif
None.gif讲解之前要先了解一个接口:IDisposable,MSDN是这样定义的:定义一种释放分配的非托管资源的方法。当托管对象不再使用时,垃圾回收器会自动释放分配给该对象的内存,不过,进行垃圾回收的时间不可预知。另外,垃圾回收器对窗口句柄、打开的文件和流等非托管资源一无所知。
None.gif
None.gif将此接口的 Dispose 方法与垃圾回收器一起使用来显式释放非托管资源。当不再需要对象时,对象的使用者可以调用此方法。非托管资源(unmanaged resource)?大致可以这样理解:类实例封装的对不受运行库管理的资源(窗口句柄、数据库连接等),这些类实例都必须实现IDisposable接口,如:SqlCommand 、SqlCeConnection、Timer等。当一个类实现IDisposable时,实例的正确用法是当对象不在需要时调用Dispose方法删除它,因此,在你实现一个类,而该类又包含其他实现IDisposable的类时,必须调用Dispose方法。这通常意味着在该类中你必须实现IDisposable。
None.gif
None.gif注:C#语言对Disposable有特殊的支持,你经常会看到如下一段代码:
None.gif
None.gif
using (SqlConnection connection  =   new  SqlConnection(connectionString))
None.gif
ExpandedBlockStart.gifContractedBlock.gif
dot.gif {
InBlock.gif
InBlock.gif    \\
do something
InBlock.gif
ExpandedBlockEnd.gif}

None.gif
None.gif这里的using就是对IDisposable接口的支持来实现对象清除。
None.gif
None.gif言归正传,下面打开CommunityServerComponents项目中Configuration文件夹中的Job.cs:
None.gif
None.gif
public   class  Job : IDisposable
None.gif
ExpandedBlockStart.gifContractedBlock.gif
dot.gif {
InBlock.gif
InBlock.gif\\代码太长…
InBlock.gif
ExpandedBlockEnd.gif}

None.gif
None.gif原来Job是实现了IDisposable的类,有了上面对IDisposable的解释不难理解,由于Job中调用了Timer,而Timer又是实现了IDisposable的类,Job类实现接口IDisposable是为了释放使用的Timer,即调用Timer中的Dispose()。看看以下的代码可以证实这一点:
None.gif
None.gif
public   void  Dispose()
None.gif
ExpandedBlockStart.gifContractedBlock.gif
dot.gif {
InBlock.gif
InBlock.gif    
if(_timer!= null && !disposed)
InBlock.gif
ExpandedSubBlockStart.gifContractedSubBlock.gif    
dot.gif{
InBlock.gif
InBlock.gif        
lock(this)
InBlock.gif
ExpandedSubBlockStart.gifContractedSubBlock.gif        
dot.gif{
InBlock.gif
InBlock.gif            _timer.Dispose();
InBlock.gif
InBlock.gif            _timer 
= null;
InBlock.gif
InBlock.gif            disposed 
= true;
InBlock.gif
ExpandedSubBlockEnd.gif        }

InBlock.gif
ExpandedSubBlockEnd.gif    }

InBlock.gif
ExpandedBlockEnd.gif}

None.gif
None.gif还有必要对Timer类做一些简单的介绍:Timer是提供以指定的时间间隔执行方法的机制,说白了就是一个定时器。Timer可以使用 TimerCallback 委托指定希望 Timer时间到达时执行的方法。也就是说,如果定时器的时间到达了,将执行TimerCallback 委托指向的方法。
None.gif
None.gif(注:创建计时器时,可以指定在第一次执行方法之前等待的时间量(截止时间)以及此后的执行期间等待的时间量(时间周期)。可以使用 Change 方法更改这些值或禁用计时器。)
None.gif
None.gif我们看一下代码:
None.gif
None.gif
private  Timer _timer  =   null ;
None.gif
None.gif
public   void  InitializeTimer(Guid id)
None.gif
ExpandedBlockStart.gifContractedBlock.gif
dot.gif {
InBlock.gif
InBlock.gif     
if(_timer == null && Enabled)
InBlock.gif
ExpandedSubBlockStart.gifContractedSubBlock.gif     
dot.gif{
InBlock.gif
InBlock.gif         _timer 
= new Timer(new TimerCallback(timer_Callback), id,Interval, Interval);
InBlock.gif
ExpandedSubBlockEnd.gif     }

InBlock.gif
ExpandedBlockEnd.gif}

None.gif
None.gif 
None.gif
None.gif
private   void  timer_Callback( object  state)
None.gif
ExpandedBlockStart.gifContractedBlock.gif
dot.gif {
InBlock.gif
InBlock.gif    Guid id 
= (Guid)state;
InBlock.gif
InBlock.gif    
if(id != Jobs.Instance().CurrentID)
InBlock.gif
ExpandedSubBlockStart.gifContractedSubBlock.gif    
dot.gif{
InBlock.gif
InBlock.gif        
this.Dispose();
InBlock.gif
InBlock.gif        
return;
InBlock.gif
ExpandedSubBlockEnd.gif    }

InBlock.gif
InBlock.gif 
InBlock.gif
InBlock.gif 
InBlock.gif
InBlock.gif 
InBlock.gif
InBlock.gif    
if(!Enabled)
InBlock.gif
InBlock.gif        
return;
InBlock.gif
InBlock.gif 
InBlock.gif
InBlock.gif    _timer.Change( Timeout.Infinite, Timeout.Infinite );
InBlock.gif
InBlock.gif 
InBlock.gif
InBlock.gif    ExecuteJob();
InBlock.gif
InBlock.gif 
InBlock.gif
InBlock.gif    
if(Enabled)
InBlock.gif
InBlock.gif        _timer.Change( Interval, Interval);
InBlock.gif
InBlock.gif    
else
InBlock.gif
InBlock.gif        
this.Dispose();
InBlock.gif
InBlock.gif 
InBlock.gif
ExpandedBlockEnd.gif}

None.gif
None.gif通过_timer 
=   new  Timer( new  TimerCallback(timer_Callback), id,Interval, Interval);
None.gif
None.gif先实例化了一个定时器,并制定了该定时器触发时候调用的方法是timer_Callback。
None.gif
None.gif(前一个Interval指:
None.gif
None.gif
None.gif后一个Interval指:
None.gif
None.gif
None.gif)
None.gif
None.gif在回调方法timer_Callback只要调用的是ExecuteJob()方法,该方法通过调用Job类的Jobs类中传递过来得xml节点信息,使用反射实例化一个具有IJob接口的类(IJob接口要求实现它的类都具有Execute方法),并且执行类中的Execute方法。我们分析一个实现了IJob的类,打开Components文件夹下的EmailJob.cs,该类中只有一个方法,就是Execute,该方法又调用Emails.cs下的SendQueuedEmails方法,主要用途是发送数据库队列中的Email(这里Email来源于用户注册时需要发送的注册成功的信息等等,通常的做法是在用户注册完毕后马上就发送Email,而CS采用的是将要发送的Email存入数据库,在一定的间隔时间后统一处理这段时间内的所有Email发送,处理的类就是EmailJob.cs)。这里还要注意一点:TimerCallback的实例不在创建计时器的线程中执行,而是在系统提供的一个单独线程池线程中执行。
None.gif
None.gif再来看看CommunityServerComponents项目下的Jobs.cs文件,这个类第一次看设计的有点蹩脚,它的构造函数是静态的,但是在静态的构造函数中实例化它自己,实例化后保存在static readonly的变量里(晕了吧,嘿嘿!)其实这叫单件模式(Singleton模式),确保全局中有且只有一个Jobs实例。Jobs的实例主要完成从CS的配置文件中读取出每个Job的配置信息,先看一下Job的配置信息:
None.gif
None.gif
< Jobs minutes  =   " 5 "  singleThread  =   " false " >
None.gif
None.gif         
< job name  =   " SiteStatisticsUpdates "  type  =   " CommunityServer.Components.SiteStatisticsJob, CommunityServer.Components "  enabled  =   " true "  enableShutDown  =   " false "   />
None.gif
None.gif         
< job name  =   " ForumsIndexing "  type  =   " CommunityServer.Discussions.Components.ForumsSearchJob, CommunityServer.Discussions "  enabled  =   " false "  enableShutDown  =   " false "   />
None.gif
None.gif         
< job name  =   " WeblogIndexing "  type  =   " CommunityServer.Blogs.Components.WeblogSearchJob, CommunityServer.Blogs "  enabled  =   " false "  enableShutDown  =   " false "   />
None.gif
None.gif         
< job name  =   " GalleryIndexing "  type  =   " CommunityServer.Galleries.Components.GallerySearchJob, CommunityServer.Galleries "  enabled  =   " false "  enableShutDown  =   " false "   />
None.gif
None.gif         
None.gif
None.gif         
< job name  =   " AnonymousUsers "  minutes  =   " 1 "  type  =   " CommunityServer.Components.AnonymousUserJob, CommunityServer.Components "  enabled  =   " true "  enableShutDown  =   " false "   />
None.gif
None.gif         
< job singleThread  =   " false "  minutes  =   " 5 "  name  =   " Emails "  type  =   " CommunityServer.Components.EmailJob, CommunityServer.Components "  enabled  =   " true "  enableShutDown  =   " false "  failureInterval  =   " 1 "  numberOfTries  =   " 10 "   />
None.gif
None.gif         
< job name  =   " Referrals "  type  =   " CommunityServer.Components.ReferralsJob, CommunityServer.Components "  enabled  =   " true "  enableShutDown  =   " false "   />
None.gif
None.gif         
< job name  =   " Views "  type  =   " CommunityServer.Components.ViewsJob, CommunityServer.Components "  enabled  =   " true "  enableShutDown  =   " false "   />
None.gif
None.gif         
< job name  =   " RecentBlogContent "  type  =   " CommunityServer.Blogs.Components.RecentContentJob, CommunityServer.Blogs "  enabled  =   " true "  enableShutDown  =   " false "   />
None.gif
None.gif         
< job name  =   " RebuildThumbnailsJob "  type  =   " CommunityServer.Galleries.Components.RebuildThumbnailsJob, CommunityServer.Galleries "  picturesPerRun  =   " 25 "  enabled  =   " true "  enableShutDown  =   " false "   />
None.gif
None.gif
</ Jobs >
None.gif
None.gif解释一下这些xml的节点意思:
None.gif
None.gif
< Jobs minutes  =   " 5 "  singleThread  =   " false " >
None.gif
None.gifminutes:执行回调函数TimerCallback的时间与时间间隔,这里是5分钟,也就是说执行回调函数在初始化请求之后的五分钟开始,并且每五分钟一次。
None.gif
None.gifsingleThread:是单线程还是多线程,前面说过为Timer创建的TimerCallback的实例不在创建计时器的线程中执行,而是在系统提供的一个单独线程池线程中执行,如果值为“
false ”就会为每个Job创建一个Timer线程,如果为“ true ”就创建一个Timer。
None.gif
None.gif
< job > 节点比较灵活,一些属性是该节点特有的,这是根据实现IJob接口类的需要决定的,以Email发送的类为例:
None.gif
None.gif
< job singleThread  =   " false "  minutes  =   " 5 "  name  =   " Emails "  type  =   " CommunityServer.Components.EmailJob, CommunityServer.Components "  enabled  =   " true "  enableShutDown  =   " false "  failureInterval  =   " 1 "  numberOfTries  =   " 10 "   />
None.gif
None.gifsingleThread:当头CS版本中没有用到。
None.gif
None.gifMinutes:执行回调函数TimerCallback的时间与时间间隔,这里是5分钟。
None.gif
None.gifname:该Job的唯一标识。
None.gif
None.giftype:该Job所在的名字空间,逗号后面的是该Job所在的程序集dll文件名称。
None.gif
None.gifenabled:如果为“
false ”该Job会被关闭,也就是不会实例化一个定时器。“ true ”则为开启该Job。
None.gif
None.gifenableShutDown:这个有点意思,用途是当该Job在执行Execute时,如果长生异常是否关闭这个Job,也就是说是否还允许它再次运行。“
false ”表示允许,“ true ”表示不允许。
None.gif
None.giffailureInterval:如果发送邮件失败,与下次尝试再次发送的时间间隔,单位是分钟。
None.gif
None.gifnumberOfTries:如果发送邮件失败,该Job会尝试几次,这里是10次。
None.gif
None.gif   现在回到我们开始时候在CSHttpModule.cs中看到的这句:
None.gif
None.gifJobs.Instance().Start();
None.gif
None.gif首先调用Jobs,实例化一个Jobs类,这个类在CS中有且只有一个实例。之后调用Jobs中的Start()方法。Jobs为了确保系统的安全,在开始之前先调用Stop(),现释放之前为Job实例化的非托管资源(其实就是调用Timer与Job下面的Dispose方法,这也是为什么Job要实现IDispose接口的原因,是想一下,如果前一次定义的定时器没有被释放,然后又接着实例化一个,然后这样多重复几次dot.gif哈哈,你的CS就会有N个线程在跑,服务器很快就会挂掉的)。
None.gif
None.gif释放完资源后(无论有没有CS都会这么做),接着就会实例化每个实现了IJob的类,根据上述的配置文件定义一个Timer或者让每个Job都定义一个自己的Timer(这就相当于给钟点工统一一个工作时间或者给每个钟点工都规定一个工作时间,规定完后该干什么的就干去吧,只要时间到了就得干活)。再往下,就自己分析吧,也就没有什么难题了…
None.gif
None.gif 
None.gif
None.gif不知道为什么我的团队中的几个成员老是不能了解这个执行过程,我解释了半天,最后发现了问题:
None.gif
None.gif回到CSHttpModule.cs文件中来,这是一个实现了IHttpModule接口的类,实现该接口的类都要有一个Init方法,我们看到,所有的Job开始初始化的起点也是从这里方法中调用的。但是我的小组成员都认为每个Http请求都回调用一次Init方法即:
None.gif
None.gif
public   void  Init(HttpApplication application) 
None.gif
ExpandedBlockStart.gifContractedBlock.gif
dot.gif
InBlock.gif
InBlock.gif    
// Wire-up application events
InBlock.gif
InBlock.gif    
//
InBlock.gif

InBlock.gif    application.BeginRequest 
+= new EventHandler(this.Application_BeginRequest);
InBlock.gif
InBlock.gif    application.AuthenticateRequest 
+= new EventHandler(Application_AuthenticateRequest);
InBlock.gif
InBlock.gif    application.Error 
+= new EventHandler(this.Application_OnError);
InBlock.gif
InBlock.gif    application.AuthorizeRequest 
+= new EventHandler(this.Application_AuthorizeRequest);
InBlock.gif
InBlock.gif    
InBlock.gif
InBlock.gif    
//settingsID = SiteSettingsManager.GetSiteSettings(application.Context).SettingsID;
InBlock.gif

InBlock.gif    Jobs.Instance().Start();
InBlock.gif
InBlock.gif    
//CSException ex = new CSException(CSExceptionType.ApplicationStart, "Appication Started " + AppDomain.CurrentDomain.FriendlyName);
InBlock.gif
InBlock.gif    
//ex.Log();
InBlock.gif

ExpandedBlockEnd.gif}

None.gif
None.gif也就是说每个请求都实例化一个CSHttpModule,如果是这样Init方法就会多次被调用,那么如果有1000个Http请求Jobs就会Start 1000次,这本来就分析不同。
None.gif
None.gif其实认为CSHttpModule会被实例化多次就是一个很大的错误,在整个CS运行过程中只会实例化一个CSHttpModule类,也就是说Init只会被执行一次。

转载于:https://www.cnblogs.com/guodapeng/archive/2007/12/21/1009076.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值