企业开发基础设施--事件通知服务(Remoting双向通信)

博客介绍了EventServer的实现,RaiseEvent方法用try...catch保证异常不影响其他服务器。事件通知服务采用中介者模式,EventClient与EventServer交互,各客户应用程序对等。还提到EventServer配置,需设remoting权限为FULL,服务端和客户端启动时都要配置Remoting。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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

public delegate void EventProcessHandler( string eventName, object eventContent);
注意,IEventNotification接口中的每个方法的第一个参数是事件名,事件名唯一标志了每个事件,它相当于一个主键。

EventClient与包含它的应用程序之间的交互通过本地事件预定/发布来完成,而与EventServer之间的交互则通过remoting完成。其实现如下:
public class EventClient:MarshalByRefObject,IEventNotification
{
private IEventNotificationeventServer = null ;
private HashtablehtableSubscribed = new Hashtable(); // eventName--Delegate(是一个链表)

public EventClient( string eventServerUri)
{
TcpChanneltheChannel
= new TcpChannel( 0 );
ChannelServices.RegisterChannel(theChannel);

this .eventServer = (IEventNotification)Activator.GetObject( typeof (IEventNotification),eventServerUri);
}

public override object InitializeLifetimeService()
{
//Remoting对象 无限生存期
return null;
}

#region IEventNotification成员
// handler是本地委托
public void SubscribeEvent( string eventName,EventProcessHandlerhandler)
{
lock ( this )
{
DelegatehandlerList
= (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,EventProcessHandlerhandler)
{
lock ( this )
{
DelegatehandlerList
= (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 )
{
DelegatehandlerList
= (Delegate) this .htableSubscribed[eventName];
if (handlerList == null )
{
return ;
}

object []args = {eventName,eventContent};
foreach (Delegatedg in handlerList.GetInvocationList())
{
try
{
dg.DynamicInvoke(args);
}
catch (Exceptionee)
{
ee
= ee;
}
}
}
}
#endregion
}


需要注意的是,EventClient从MarshalByRefObject继承,这是因为,当EventServer上有事件被触发时,也会通过Remoting Event来通知EventClient,这个时候,EventClient就是一个remoting object。另外,OnRemoteEventHappen方法必须为public,因为这个方法将会被EventServer远程调用。

下面给出EventServer的实现:
public class EventServer:MarshalByRefObject,IEventNotification
{
// htableSubscribed内部每项的Delegate链表中每一个委托都是透明代理
private HashtablehtableSubscribed = new Hashtable(); // eventName--Delegate(是一个链表)
public EventServer()
{
}

public override object InitializeLifetimeService()
{
//Remoting对象 无限生存期
return null;
}


#region IEventNotification成员
// handler是一个透明代理,指向EventClient.OnRemoteEventHappen委托
public void SubscribeEvent( string eventName,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;
}
}

public void UnSubscribeEvent( string eventName,EventProcessHandlerhandler)
{
lock ( this )
{
DelegatehandlerList
= (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 )
{
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 >
< channel ref ="tcp" 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 >

请特别注意,标志为红色的两句。并且,在服务端程序启动时,配置Remoting:

RemotingConfiguration.Configure("EventClient.exe.config");

由于在服务端回调Client时,Client相对变成"Server",所以,Client也必须注册一个remoting通道。

< system .runtime.remoting >
< application >
< channels >
< channel ref ="tcp" port ="0" >
< clientProviders >
< formatter ref ="binary" />
</ clientProviders >
< serverProviders >
< formatter ref ="binary" typeFilterLevel ="Full" />
</ serverProviders >
</ channel >
</ channels >
</ application >
</ system.runtime.remoting >

并且,在客户端程序启动时,配置Remoting:

RemotingConfiguration.Configure("EventClient.exe.config");

企业开发基础设施 主目录
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值