本文采用的解决方案中,有两个重要组件:事件服务器EventServer和事件客户端EventClient。EventServer作为中介者,并作为一个独立的系统,通常可以将其作为windows服务运行。EventServer和EventClient之间的关系如下所示:
每个需要事件通知的应用程序中,都包含了EventClient组件,应用程序通过EventClient与事件服务器进行交互,而当有事件发生时,EventClient也会触发相应的事件来通知应用程序。
EventServer和EventClient实现了共同的接口IEventNotification:
publicinterfaceIEventNotification
{
voidSubscribeEvent(stringeventName,EventProcessHandlerhandler);//预定事件
voidUnSubscribeEvent(stringeventName,EventProcessHandlerhandler);//取消预定
voidRaiseEvent(stringeventName,objecteventContent);//发布事件
}
publicdelegatevoidEventProcessHandler(stringeventName,objecteventContent);
注意,IEventNotification接口中的每个方法的第一个参数是事件名,事件名唯一标志了每个事件,它相当于一个主键。{
voidSubscribeEvent(stringeventName,EventProcessHandlerhandler);//预定事件
voidUnSubscribeEvent(stringeventName,EventProcessHandlerhandler);//取消预定
voidRaiseEvent(stringeventName,objecteventContent);//发布事件
}
publicdelegatevoidEventProcessHandler(stringeventName,objecteventContent);
EventClient与包含它的应用程序之间的交互通过本地事件预定/发布来完成,而与EventServer之间的交互则通过remoting完成。其实现如下:
publicclassEventClient:MarshalByRefObject,IEventNotification
{
privateIEventNotificationeventServer=null;
privateHashtablehtableSubscribed=newHashtable();//eventName--Delegate(是一个链表)
publicEventClient(stringeventServerUri)
{
TcpChanneltheChannel=newTcpChannel(0);
ChannelServices.RegisterChannel(theChannel);
this.eventServer=(IEventNotification)Activator.GetObject(typeof(IEventNotification),eventServerUri);
}
public override object InitializeLifetimeService()
{
//Remoting对象 无限生存期
return null;
}
#regionIEventNotification成员
//handler是本地委托
publicvoidSubscribeEvent(stringeventName,EventProcessHandlerhandler)
{
lock(this)
{
DelegatehandlerList=(Delegate)this.htableSubscribed[eventName];
if(handlerList==null)
{
this.htableSubscribed.Add(eventName,handler);
this.eventServer.SubscribeEvent(eventName,newEventProcessHandler(this.OnRemoteEventHappen));
return;
}
handlerList=Delegate.Combine(handlerList,handler);
this.htableSubscribed[eventName]=handlerList;
}
}
publicvoidUnSubscribeEvent(stringeventName,EventProcessHandlerhandler)
{
lock(this)
{
DelegatehandlerList=(Delegate)this.htableSubscribed[eventName];
if(handlerList!=null)
{
handlerList=Delegate.Remove(handlerList,handler);
this.htableSubscribed[eventName]=handlerList;
}
}
}
publicvoidRaiseEvent(stringeventName,objecteventContent)
{
this.eventServer.RaiseEvent(eventName,eventContent);
}
#endregion
#regionOnRemoteEventHappen
///<summary>
///当EventServer上有事件触发时,EventServer会转换为客户端,而EventClient变成远程对象,
///该方法会被远程调用。所以必须为public
///</summary>
publicvoidOnRemoteEventHappen(stringeventName,objecteventContent)
{
lock(this)
{
DelegatehandlerList=(Delegate)this.htableSubscribed[eventName];
if(handlerList==null)
{
return;
}
object[]args={eventName,eventContent};
foreach(DelegatedginhandlerList.GetInvocationList())
{
try
{
dg.DynamicInvoke(args);
}
catch(Exceptionee)
{
ee=ee;
}
}
}
}
#endregion
}
{
privateIEventNotificationeventServer=null;
privateHashtablehtableSubscribed=newHashtable();//eventName--Delegate(是一个链表)
publicEventClient(stringeventServerUri)
{
TcpChanneltheChannel=newTcpChannel(0);
ChannelServices.RegisterChannel(theChannel);
this.eventServer=(IEventNotification)Activator.GetObject(typeof(IEventNotification),eventServerUri);
}
public override object InitializeLifetimeService()
{
//Remoting对象 无限生存期
return null;
}
#regionIEventNotification成员
//handler是本地委托
publicvoidSubscribeEvent(stringeventName,EventProcessHandlerhandler)
{
lock(this)
{
DelegatehandlerList=(Delegate)this.htableSubscribed[eventName];
if(handlerList==null)
{
this.htableSubscribed.Add(eventName,handler);
this.eventServer.SubscribeEvent(eventName,newEventProcessHandler(this.OnRemoteEventHappen));
return;
}
handlerList=Delegate.Combine(handlerList,handler);
this.htableSubscribed[eventName]=handlerList;
}
}
publicvoidUnSubscribeEvent(stringeventName,EventProcessHandlerhandler)
{
lock(this)
{
DelegatehandlerList=(Delegate)this.htableSubscribed[eventName];
if(handlerList!=null)
{
handlerList=Delegate.Remove(handlerList,handler);
this.htableSubscribed[eventName]=handlerList;
}
}
}
publicvoidRaiseEvent(stringeventName,objecteventContent)
{
this.eventServer.RaiseEvent(eventName,eventContent);
}
#endregion
#regionOnRemoteEventHappen
///<summary>
///当EventServer上有事件触发时,EventServer会转换为客户端,而EventClient变成远程对象,
///该方法会被远程调用。所以必须为public
///</summary>
publicvoidOnRemoteEventHappen(stringeventName,objecteventContent)
{
lock(this)
{
DelegatehandlerList=(Delegate)this.htableSubscribed[eventName];
if(handlerList==null)
{
return;
}
object[]args={eventName,eventContent};
foreach(DelegatedginhandlerList.GetInvocationList())
{
try
{
dg.DynamicInvoke(args);
}
catch(Exceptionee)
{
ee=ee;
}
}
}
}
#endregion
}
需要注意的是,EventClient从MarshalByRefObject继承,这是因为,当EventServer上有事件被触发时,也会通过Remoting Event来通知EventClient,这个时候,EventClient就是一个remoting object。另外,OnRemoteEventHappen方法必须为public,因为这个方法将会被EventServer远程调用。
下面给出EventServer的实现:
publicclassEventServer:MarshalByRefObject,IEventNotification
{
//htableSubscribed内部每项的Delegate链表中每一个委托都是透明代理
privateHashtablehtableSubscribed=newHashtable();//eventName--Delegate(是一个链表)
publicEventServer()
{
}
public override object InitializeLifetimeService()
{
//Remoting对象 无限生存期
return null;
}
#regionIEventNotification成员
//handler是一个透明代理,指向EventClient.OnRemoteEventHappen委托
publicvoidSubscribeEvent(stringeventName,EventProcessHandlerhandler)
{
lock(this)
{
DelegatehandlerList=(Delegate)this.htableSubscribed[eventName];
if(handlerList==null)
{
this.htableSubscribed.Add(eventName,handler);
return;
}
handlerList=Delegate.Combine(handlerList,handler);
this.htableSubscribed[eventName]=handlerList;
}
}
publicvoidUnSubscribeEvent(stringeventName,EventProcessHandlerhandler)
{
lock(this)
{
DelegatehandlerList=(Delegate)this.htableSubscribed[eventName];
if(handlerList!=null)
{
handlerList=Delegate.Remove(handlerList,handler);
this.htableSubscribed[eventName]=handlerList;
}
}
}
publicvoidRaiseEvent(stringeventName,objecteventContent)
{
lock(this)
{
DelegatehandlerList=(Delegate)this.htableSubscribed[eventName];
if(handlerList==null)
{
return;
}
object[]args={eventName,eventContent};
IEnumeratorenumerator=handlerList.GetInvocationList().GetEnumerator();
while(enumerator.MoveNext())
{
Delegatehandler=(Delegate)enumerator.Current;
try
{
handler.DynamicInvoke(args);
}
catch(Exceptionee)//也可重试
{
ee=ee;
handlerList=Delegate.Remove(handlerList,handler);
this.htableSubscribed[eventName]=handlerList;
}
}
}
}
#endregion
}
{
//htableSubscribed内部每项的Delegate链表中每一个委托都是透明代理
privateHashtablehtableSubscribed=newHashtable();//eventName--Delegate(是一个链表)
publicEventServer()
{
}
public override object InitializeLifetimeService()
{
//Remoting对象 无限生存期
return null;
}
#regionIEventNotification成员
//handler是一个透明代理,指向EventClient.OnRemoteEventHappen委托
publicvoidSubscribeEvent(stringeventName,EventProcessHandlerhandler)
{
lock(this)
{
DelegatehandlerList=(Delegate)this.htableSubscribed[eventName];
if(handlerList==null)
{
this.htableSubscribed.Add(eventName,handler);
return;
}
handlerList=Delegate.Combine(handlerList,handler);
this.htableSubscribed[eventName]=handlerList;
}
}
publicvoidUnSubscribeEvent(stringeventName,EventProcessHandlerhandler)
{
lock(this)
{
DelegatehandlerList=(Delegate)this.htableSubscribed[eventName];
if(handlerList!=null)
{
handlerList=Delegate.Remove(handlerList,handler);
this.htableSubscribed[eventName]=handlerList;
}
}
}
publicvoidRaiseEvent(stringeventName,objecteventContent)
{
lock(this)
{
DelegatehandlerList=(Delegate)this.htableSubscribed[eventName];
if(handlerList==null)
{
return;
}
object[]args={eventName,eventContent};
IEnumeratorenumerator=handlerList.GetInvocationList().GetEnumerator();
while(enumerator.MoveNext())
{
Delegatehandler=(Delegate)enumerator.Current;
try
{
handler.DynamicInvoke(args);
}
catch(Exceptionee)//也可重试
{
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>
<channelref="tcp"port="8888">
<serverProviders>
<providerref="wsdl"/>
<formatterref="soap"typeFilterLevel="Full"/>
<formatterref="binary"typeFilterLevel="Full"/>
</serverProviders>
<clientProviders>
<formatterref="binary"/>
</clientProviders>
</channel>
</channels>
</application>
</system.runtime.remoting>
</configuration>
<system.runtime.remoting>
<application>
<service>
<wellknown
mode="Singleton"
type="EnterpriseServerBase.XFramework.EventNotification.EventServer,EnterpriseServerBase"
objectUri="SerInfoRemote"/>
</service>
<channels>
<channelref="tcp"port="8888">
<serverProviders>
<providerref="wsdl"/>
<formatterref="soap"typeFilterLevel="Full"/>
<formatterref="binary"typeFilterLevel="Full"/>
</serverProviders>
<clientProviders>
<formatterref="binary"/>
</clientProviders>
</channel>
</channels>
</application>
</system.runtime.remoting>
</configuration>
请特别注意,标志为红色的两句。并且,在服务端程序启动时,配置Remoting:
RemotingConfiguration.Configure("EventClient.exe.config");
由于在服务端回调Client时,Client相对变成"Server",所以,Client也必须注册一个remoting通道。
<system.runtime.remoting>
<application>
<channels>
<channelref="tcp"port="0">
<clientProviders>
<formatterref="binary"/>
</clientProviders>
<serverProviders>
<formatterref="binary"typeFilterLevel="Full"/>
</serverProviders>
</channel>
</channels>
</application>
</system.runtime.remoting>
<application>
<channels>
<channelref="tcp"port="0">
<clientProviders>
<formatterref="binary"/>
</clientProviders>
<serverProviders>
<formatterref="binary"typeFilterLevel="Full"/>
</serverProviders>
</channel>
</channels>
</application>
</system.runtime.remoting>
并且,在客户端程序启动时,配置Remoting:
RemotingConfiguration.Configure("EventClient.exe.config");
企业开发基础设施 主目录