您的位置:知识库 » .NET技术

基于消息与.Net Remoting的分布式处理架构

作者: 张逸  来源: 博客园  发布时间: 2010-08-18 11:13  阅读: 2157 次  推荐: 0   原文链接   [收藏]  
摘要:分布式处理在大型企业应用系统中,最大的优势是将负载分布。通过多台服务器处理多个任务,以优化整个系统的处理能力和运行效率。分布式处理的技术核心是完成服务与服务之间、服务端与客户端之间的通信。
[1] 基于消息与.Net Remoting的分布式处理架构
[2] 基于消息与.Net Remoting的分布式处理架构

      分布式处理在大型企业应用系统中,最大的优势是将负载分布。通过多台服务器处理多个任务,以优化整个系统的处理能力和运行效率。分布式处理的技术核心是完成服务与服务之间、服务端与客户端之间的通信。在.Net 1.1中,可以利用Web Service或者.Net Remoting来实现服务进程之间的通信。本文将介绍一种基于消息的分布式处理架构,利用了.Net Remoting技术,并参考了CORBA Naming Service的处理方式,且定义了一套消息体制,来实现分布式处理。 

   一、消息的定义

       要实现进程间的通信,则通信内容的载体——消息,就必须在服务两端具有统一的消息标准定义。从通信的角度来看,消息可以分为两类:Request Messge和Reply Message。为简便起见,这两类消息可以采用同样的结构。

       消息的主体包括ID,Name和Body,我们可以定义如下的接口方法,来获得消息主体的相关属性:

public interface IMessage:ICloneable
{
IMessageItemSequence GetMessageBody();
string GetMessageID();
string GetMessageName();
void SetMessageBody(IMessageItemSequence aMessageBody);
void SetMessageID(string aID);
void SetMessageName(string aName);
}

    消息主体类Message实现了IMessage接口。在该类中,消息体Body为IMessageItemSequence类型。这个类型用于Get和Set消息的内容:Value和Item:

public interface IMessageItemSequence:ICloneable
{
IMessageItem GetItem(
string aName);
void SetItem(string aName,IMessageItem aMessageItem);
string GetValue(string aName);
void SetValue(string aName,string aValue);
}

       Value为string类型,并利用HashTable来存储Key和Value的键值对。而Item则为IMessageItem类型,同样的在IMessageItemSequence的实现类中,利用HashTable存储了Key和Item的键值对。

       IMessageItem支持了消息体的嵌套。它包含了两部分:SubValue和SubItem。实现的方式和IMessageItemSequence相似。定义这样的嵌套结构,使得消息的扩展成为可能。一般的结构如下:

       IMessage——Name
                     ——ID
                     ——Body(IMessageItemSequence)
                            ——Value
                            ——Item(IMessageItem)
                                   ——SubValue
                                   ——SubItem(IMessageItem)
                                          ——……

       各个消息对象之间的关系如下:

Distribute1.gif


       在实现服务进程通信之前,我们必须定义好各个服务或各个业务的消息格式。通过消息体的方法在服务的一端设置消息的值,然后发送,并在服务的另一端获得这些值。例如发送消息端定义如下的消息体:

IMessageFactory factory = new MessageFactory();
IMessageItemSequence body
= factory.CreateMessageItemSequence();
body.SetValue(
"name1","value1");
body.SetValue(
"name2","value2");
IMessageItem item
= factory.CreateMessageItem();
item.SetSubValue(
"subname1","subvalue1");
item.SetSubValue(
"subname2","subvalue2");
IMessageItem subItem1
= factory.CreateMessageItem();
subItem1.SetSubValue(
"subsubname11","subsubvalue11");
subItem1.SetSubValue(
"subsubname12","subsubvalue12");
IMessageItem subItem2
= factory.CreateMessageItem();
subItem1.SetSubValue(
"subsubname21","subsubvalue21");
subItem1.SetSubValue(
"subsubname22","subsubvalue22");
item.SetSubItem(
"subitem1",subItem1);
item.SetSubItem(
"subitem2",subItem2);
body.SetItem(
"item",item);
//Send Request Message
MyServiceClient service = new MyServiceClient("Client");
IMessageItemSequence reply
= service.SendRequest("TestService","Test1",body);

       在接收消息端就可以通过获得body的消息体内容,进行相关业务的处理。 

   二、.Net Remoting服务

       在.Net中要实现进程间的通信,主要是应用Remoting技术。根据前面对消息的定义可知,实际上服务的实现,可以认为是对消息的处理。因此,我们可以对服务进行抽象,定义接口IService:

public interface IService
{
IMessage Execute(IMessage aMessage);
}

       Execute()方法接受一条Request Message,对其进行处理后,返回一条Reply Message。在整个分布式处理架构中,可以认为所有的服务均实现该接口。但受到Remoting技术的限制,如果要实现服务,则该服务类必须继承自MarshalByRefObject,同时必须在服务端被Marshal。随着服务类的增多,必然要在服务两端都要对这些服务的信息进行管理,这加大了系统实现的难度与管理的开销。如果我们从另外一个角度来分析服务的性质,基于消息处理而言,所有服务均是对Request Message的处理。我们完全可以定义一个Request服务负责此消息的处理。

       然而,Request服务处理消息的方式虽然一致,但毕竟服务实现的业务,即对消息处理的具体实现,却是不相同的。对我们要实现的服务,可以分为两大类:业务服务与Request服务。实现的过程为:首先,具体的业务服务向Request服务发出Request请求,Request服务侦听到该请求,然后交由其侦听的服务来具体处理。

       业务服务均具有发出Request请求的能力,且这些服务均被Request服务所侦听,因此我们可以为业务服务抽象出接口

IListenService:
public interface IListenService
{
IMessage OnRequest(IMessage aMessage);
}
//Request服务实现了IService接口,
//并包含IListenService类型对象的委派,以执行OnRequest()方法:
public class RequestListener:MarshalByRefObject,IService
{
public RequestListener(IListenService listenService)
{
m_ListenService
= listenService;
}
private IListenService m_ListenService;
#region IService Members
public IMessage Execute(IMessage aMessage)
{
return this.m_ListenService.OnRequest(aMessage);
}
#endregion
public override object InitializeLifetimeService()
{
return null;
}
}

       在RequestListener服务中,继承了MarshalByRefObject类,同时实现了IService接口。通过该类的构造函数,接收IListService对象。

       由于Request消息均由Request服务即RequestListener处理,因此,业务服务的类均应包含一个RequestListener的委派,唯一的区别是其服务名不相同。业务服务类实现IListenService接口,但不需要继承MarshalByRefObject,因为被Marshal的是该业务服务内部的RequestListener对象,而非业务服务本身:

public abstract class Service:IListenService
{
public Service(string serviceName)
{
m_ServiceName
= serviceName;
m_RequestListener
= new RequestListener(this);
}
#region IListenService Members
public IMessage OnRequest(IMessage aMessage)
{
//……
}
#endregion
private string m_ServiceName;
private RequestListener m_RequestListener;
}

       Service类是一个抽象类,所有的业务服务均继承自该类。最后的服务架构如下:

Distribute2.gif


       我们还需要在Service类中定义发送Request消息的行为,通过它,才能使业务服务被RequestListener所侦听。

public IMessageItemSequence SendRequest(string aServiceName,
string
aMessageName,IMessageItemSequence aMessageBody)
{
IMessage message
= m_Factory.CreateMessage();
message.SetMessageName(aMessageName);
message.SetMessageID(
"");
message.SetMessageBody(aMessageBody);
IService service
= FindService(aServiceName);
IMessageItemSequence replyBody
= m_Factory.CreateMessageItemSequence();
if (service != null)
{
IMessage replyMessage
= service.Execute(message);
replyBody
= replyMessage.GetMessageBody();
}
else
{
replyBody.SetValue(
"result","Failure");
}
return replyBody;
}

       注意SendRequest()方法的定义,其参数包括服务名,消息名和被发送的消息主体。而在实现中最关键的一点是FindService()方法。我们要查找的服务正是与之对应的RequestListener服务。不过,在此之前,我们还需要先将服务Marshal:    

public void Initialize()
{
RemotingServices.Marshal(
this.m_RequestListener,
this
.m_ServiceName + ".RequestListener");
}

       我们Marshal的对象,是业务服务中的Request服务对象m_RequestListener,这个对象在Service的构造函数中被实例化:

m_RequestListener = new RequestListener(this);

       注意,在实例化的时候是将this作为IListenService对象传递给RequestListener。因此,此时被Marshal的服务对象,保留了业务服务本身即Service的指引。可以看出,在Service和RequestListener之间,采用了“双重委派”的机制。

       通过调用Initialize()方法,初始化了一个服务对象,其类型为RequestListener(或IService),其服务名为:Service的服务名 + ".RequestListener"。而该服务正是我们在SendRequest()方法中要查找的Service:

IService service = FindService(aServiceName);

       下面我们来看看FindService()方法的实现:

protected IService FindService(string aServiceName)
{
lock (this.m_Services)
{
IService service
= (IService)m_Services[aServiceName];
if (service != null)
{
return service;
}
else
{
IService tmpService
= GetService(aServiceName);
AddService(aServiceName,tmpService);
return tmpService;
}
}
}

       可以看到,这个服务是被添加到m_Service对象中,该对象为SortedList类型,服务名为Key,IService对象为Value。如果没有找到,则通过私有方法GetService()来获得:

private IService GetService(string aServiceName)
{
IService service
= (IService)Activator.GetObject(typeof(RequestListener),
"tcp://localhost:9090/" + aServiceName + ".RequestListener");
return service;
}

       在这里,Channel、IP、Port应该从配置文件中获取,为简便起见,这里直接赋为常量。

       再分析SendRequest方法,在找到对应的服务后,执行了IService的Execute()方法。此时的IService为RequestListener,而从前面对RequestListener的定义可知,Execute()方法执行的其实是其侦听的业务服务的OnRequest()方法。

       我们可以定义一个具体的业务服务类,来分析整个消息传递的过程。该类继承于Service抽象类:

public class MyService:Service
{
public MyService(string aServiceName):base(aServiceName)
{}
}

       假设把进程分为服务端和客户端,那么对消息处理的步骤如下:

 1、 在客户端调用MyService的SendRequest()方法发送Request消息;
 2、 查找被Marshal的服务,即RequestListener对象,此时该对象应包含对应的业务服务对象MyService;
 3、 在服务端调用RequestListener的Execute()方法。该方法则调用业务服务MyService的OnRequest()方法。

在这些步骤中,除了第一步在客户端执行外,其他的步骤均是在服务端进行。

[第1页][第2页]
0
0

.NET技术热门文章

    .NET技术最新文章

      最新新闻

        热门新闻