gaoxiang

专注于.NET技术

博客园 首页 新随笔 联系 订阅 管理
事件通知服务用于解决多个应用程序之间的事件发布与预定的问题。在.NET平台上,跨应用程序的事件发布/预定通常以Remoting作为底层的通信基础,在此基础之上,事件通知服务使用中介者模式来简化跨应用程序的事件通知问题。
    本文采用的解决方案中,有两个重要组件:事件服务器EventServer和事件客户端EventClient。EventServer作为中介者,并作为一个独立的系统,通常可以将其作为windows服务运行。EventServer和EventClient之间的关系如下所示:
    
    每个需要事件通知的应用程序中,都包含了EventClient组件,应用程序通过EventClient与事件服务器进行交互,而当有事件发生时,EventClient也会触发相应的事件来通知应用程序。
    EventServer和EventClient实现了共同的接口IEventNotification:
    public interface IEventNotification
    {
        
void SubscribeEvent(string eventName ,EventProcessHandler handler) ;//预定事件
        void UnSubscribeEvent(string eventName ,EventProcessHandler handler) ;//取消预定
        void RaiseEvent(string eventName ,object eventContent) ; //发布事件
    }

    
public delegate void EventProcessHandler(string eventName ,object eventContent) ;
    注意,IEventNotification接口中的每个方法的第一个参数是事件名,事件名唯一标志了每个事件,它相当于一个主键。
    
    EventClient与包含它的应用程序之间的交互通过本地事件预定/发布来完成,而与EventServer之间的交互则通过remoting完成。其实现如下:    
    public class EventClient :MarshalByRefObject ,IEventNotification
    {
        
private IEventNotification eventServer = null ;
        
private Hashtable htableSubscribed       = new Hashtable() ; //eventName -- Delegate(是一个链表)

        
public EventClient(string eventServerUri)
        {            
            TcpChannel theChannel 
= new  TcpChannel(0) ;
            ChannelServices.RegisterChannel(theChannel) ;        
            
            
this.eventServer = (IEventNotification)Activator.GetObject(typeof(IEventNotification) ,eventServerUri);
        }        

        
#region IEventNotification 成员
        
//handler是本地委托
        public void SubscribeEvent(string eventName, EventProcessHandler handler)
        {        
            
lock(this)
            {
                Delegate handlerList 
= (Delegate)this.htableSubscribed[eventName] ;
                
if(handlerList == null)
                {
                    
this.htableSubscribed.Add(eventName ,handler) ;            
                    
this.eventServer.SubscribeEvent(eventName ,new EventProcessHandler(this.OnRemoteEventHappen)) ;
                    
return ;
                }
                
                handlerList 
= Delegate.Combine(handlerList ,handler) ;
                
this.htableSubscribed[eventName] = handlerList ;
            }
        }

        
public void UnSubscribeEvent(string eventName, EventProcessHandler handler)
        {        
            
lock(this)
            {
                Delegate handlerList 
= (Delegate)this.htableSubscribed[eventName] ;

                
if(handlerList != null)
                {
                    handlerList 
= Delegate.Remove(handlerList ,handler) ;
                    
this.htableSubscribed[eventName] = handlerList ;
                }
            }
        }

        
public void RaiseEvent(string eventName, object eventContent)
        {    
            
this.eventServer.RaiseEvent(eventName ,eventContent) ;
        }        
        
#endregion

        
#region OnRemoteEventHappen
        
/// <summary>
        
/// 当EventServer上有事件触发时,EventServer会转换为客户端,而EventClient变成远程对象,
        
/// 该方法会被远程调用。所以必须为public
        
/// </summary>        
        public void OnRemoteEventHappen(string eventName, object eventContent)
        {
            
lock(this)
            {
                Delegate handlerList 
= (Delegate)this.htableSubscribed[eventName] ;
                
if(handlerList == null)
                {
                    
return ;
                }
            
                
object[] args = {eventName ,eventContent} ;
                
foreach(Delegate dg in handlerList.GetInvocationList())
                {
                    
try
                    {                    
                        dg.DynamicInvoke(args) ;
                    }
                    
catch(Exception ee)
                    {
                        ee 
= ee ;
                    }
                }
            }
        }
        
#endregion
    }

     
    需要注意的是,EventClient从MarshalByRefObject继承,这是因为,当EventServer上有事件被触发时,也会通过Remoting Event来通知EventClient,这个时候,EventClient就是一个remoting object。另外,OnRemoteEventHappen方法必须为public,因为这个方法将会被EventServer远程调用。
    
    下面给出EventServer的实现:
    public class EventServer :MarshalByRefObject ,IEventNotification    
    {
        
//htableSubscribed内部每项的Delegate链表中每一个委托都是透明代理
        private Hashtable htableSubscribed = new Hashtable() ; //eventName -- Delegate(是一个链表)
        public EventServer()
        {
        }

        
#region IEventNotification 成员
        
//handler是一个透明代理,指向EventClient.OnRemoteEventHappen委托
        public void SubscribeEvent(string eventName, EventProcessHandler handler)
        {            
            
lock(this)
            {
                Delegate handlerList 
= (Delegate)this.htableSubscribed[eventName] ;

                
if(handlerList == null)
                {
                    
this.htableSubscribed.Add(eventName ,handler) ;                    
                    
return ;
                }
                
                handlerList 
= Delegate.Combine(handlerList ,handler) ;
                
this.htableSubscribed[eventName] = handlerList ;
            }
        }

        
public void UnSubscribeEvent(string eventName, EventProcessHandler handler)
        {            
            
lock(this)
            {
                Delegate handlerList 
= (Delegate)this.htableSubscribed[eventName] ;

                
if(handlerList != null)
                {
                    handlerList 
= Delegate.Remove(handlerList ,handler) ;
                    
this.htableSubscribed[eventName] = handlerList ;
                }
            }
        }

        
public void RaiseEvent(string eventName, object eventContent)
        {    
            
lock(this)
            {
                Delegate handlerList 
= (Delegate)this.htableSubscribed[eventName] ;
                
if(handlerList == null)
                {
                    
return ;
                }

                
object[] args = {eventName ,eventContent} ;
                IEnumerator enumerator 
= handlerList.GetInvocationList().GetEnumerator() ;
                
while(enumerator.MoveNext())
                {
                    Delegate handler 
= (Delegate)enumerator.Current ;
                    
try
                    {
                        handler.DynamicInvoke(args) ;
                    }
                    
catch(Exception ee) //也可重试
                    {
                        ee 
= ee ;
                        handlerList 
= Delegate.Remove(handlerList ,handler) ;
                        
this.htableSubscribed[eventName] = handlerList ;
                    }
                }
            }
        }

        
#endregion
    }

    EventServer的实现是很容易理解的,需要注意的是RaiseEvent方法,该方法在while循环中对每个循环加入了try...catch,这是为了保证,当一个应用程序无法接收通知或接收通知失败时不会影响到其它的服务器。
    
    关于事件通知服务,可以总结为以下几点:
(1)事件通知服务采用了中介者模式,所有的EventClient只与EventServer(中介者)交互,从EventServer处预定名为eventName的事件,或发布名为eventName的事件。
(2)各个客户应用程序是对等的,它们都可以预定事件和发布事件。
(3)EventServer不会自主地触发事件,它就像一个公共区(缓存预定者)或转发器(广播事件)。
(4)EventServer 将在事件服务器上作为远程对象发布
(5)客户应用程序将通过EventClient来预定事件、发布事件。

    最后,需要提出的是关于事件服务器的配置,需要将remoting的权限级别设置为FULL,否则,就会出现事件句柄无法序列化的异常。在我的示例中,EventServer的配置文件如下:
<configuration>
  <system.runtime.remoting>
    <application>
      <service>
        <wellknown
  mode="Singleton"
  type="EnterpriseServerBase.XFramework.EventNotification.EventServer,EnterpriseServerBase"
  objectUri="SerInfoRemote" />
      </service>
      <channels>
        <channel ref="http" port="8888">         
          <serverProviders>
            <provider ref="wsdl" /> 
            <formatter ref="soap" typeFilterLevel="Full" />
            <formatter ref="binary" typeFilterLevel="Full" /> 
          </serverProviders>
   <clientProviders>
           <formatter ref="binary" />
          </clientProviders>
        </channel>
      </channels>
    </application>
  </system.runtime.remoting>
</configuration>

    请特别注意,标志为红色的两句。           

       企业开发基础设施  主目录

Feedback

# re: 企业开发基础设施--事件通知服务  回复   

2005-09-26 22:48 by 奇思软件
pretty good

# re: 企业开发基础设施--事件通知服务  回复   

2005-09-27 09:02 by andyloo
能否举个例子讲一下具体的应用场景,否则不大好理解呢,这个是用来在不同应用程序之间进行通讯的吗?

# re: 企业开发基础设施--事件通知服务  回复   

2005-09-27 10:20 by AllenXie-MSTC
楼主没有说明EventServer中的事件和EventClient中事件的区别:
1.EventServer中主题(eventName)对应的事件所起的作用是:通知已订阅本主题的各EventClient端某个主题(eventName)已触发.
2.EventClient中存放的主题(eventName)对应的本地事件列表,接到EventServer的通知后,逐个进行处理..在某种意义上说EventClient也服务端..充当中转服务器的角色.
此外:再说明一下EventClient中的UnSubscribeEvent缺少对下列情况的处理:
当EventClient中某主题的事件列表为空时,应该调用EventServer的UnSubScribeEvent方法..


一点自己的想法,望楼主指导....

# re: 企业开发基础设施--事件通知服务  回复   

2005-09-27 11:57 by zhuweisky
To AllenXie-MSTC :
你补充的很好,本文实现的是一个基本的原型,如果真的要在开发中应用,还需在此基础上做更多的工作,这个就需要大家动手自己来完成了:)

# re: 企业开发基础设施--事件通知服务  回复   

2005-09-27 17:44 by 乱发吹风
<应用框架程序的设计与实现------.NET平台>这本书,与你关注的主题类似

# re: 企业开发基础设施--事件通知服务  回复   

2005-09-30 15:50 by wang2855
举个例子吧。
posted on 2006-05-18 15:31  S孤单一吻S  阅读(283)  评论(0)    收藏  举报