基于ICE中间件的RPC通信的设计与实现.docx
《基于ICE中间件的RPC通信的设计与实现.docx》由会员分享,可在线阅读,更多相关《基于ICE中间件的RPC通信的设计与实现.docx(21页珍藏版)》请在冰豆网上搜索。
基于ICE中间件的RPC通信的设计与实现
基于ICE中间件的RPC通信的设计与实现
1、背景
在系统各模块的设计中,存在着诸多的远程接口,比如前端操作显示软件与后端调度服务软件之间、后端设计服务软件与订阅管理软件之间、后端调度服务软件与计算节点之间、计算节点与计算节点之间等都是通过RPC方式来完成的。
为此必须对整个系统的RPC调用构思一种通用的方案,且能够很方便实现各接口。
本文的目的正是探索一种通用的解决方案,对整个平台的RPC通信提供支持。
由于移植现有的NMSTL中的PRC存在如下不足:
一、移植NMSTL的RPC时间长,ICE直接学会并使用即可。
二、为了在Windows和Unix平台保持系统通用。
移植的NMSTL只能在Windows下或Unix下用。
为了克服以上缺点,选择ICE中间件,利用其提供的RPC机制设计方案。
2、方案设计
2.1接口设计
此部分主要用于描述服务器与客户端进行通信的接口,使用ICE中间件平台专用的Slice来进行定义(具体参照Slice语法规范)。
这是一种用于使对象接口与其实现相分离的基础性抽象机制,在客户与服务器之间建立合约,描述应用所使用的各种类型及对象接口。
利用编译器将这种与语言无关的定义翻译成针对特定语言的类型定义和API,这里利用slice2cpp编译器将slice定义映射成服务器端的框架文件和客户端的代理文件。
如有远程接口voidadd_xml_string(stringxml);用slice语言描述如下:
demo.ice(其中.ice是slice接口描述文件的后缀名)
moduledemo
{
interfaceRpcMethod
{
voidaddXmlStringRpc(stringxml);
};
};
说明:
moduledemo表示接口的命名空间,每个interface编译后都会生成一个抽象class与之对应,interface中的每一个方法都会生成在抽象类中有一个纯虚函数与之对应。
接下面用编译器将demo.ice编译成存根和框架文件,
会生成demo.h和demo.cpp两文件。
2.2服务器端设计
2.2.1服务器端功能设计
(1)服务Servant的实现:
也就是接口设计中所描述操作的具体实现。
对于在slice文件中所定义的每个interface都会在框架中生成一个具体的抽象类class,而对于接口中每个操作都会在对应的抽象类中生成一个纯虚函数。
因此服务Servant的实现过程就是去继承此抽象类,然后具体地去实现这些纯虚函数即可。
如上述2-1中的demo.ice编译后生成框架中包含一个对应类RpcMethod:
classRpcMethod:
virtualpublic:
:
Ice:
:
Object
{
public:
virtualvoidaddXmlStringRpc(const:
:
std:
:
string&,const:
:
Ice:
:
Current&=:
:
Ice:
:
Current())=0;
……
}
服务Servant的实现即继承上面的抽象类并实现其纯虚函数,具体来说:
classRpcMethodI:
publicRpcMethod{
public:
virtualvoidrpcAddXmlString(const:
:
std:
:
string&xml,const:
:
Ice:
:
Current¤t){
//这里面是业务代码,即具体RPC方法的应用逻辑
……..
}
}
(2)服务端RPC通信环境的建立:
包括ICE通信器的建立、对象适配器的产生、服务Servant服务的注册等。
服务端RPC通信环境的建立分为如下几个步骤:
1服务端ICE通信器即Ice:
:
Communicator的建立:
通信器的一个实例是与服务器端一些运行资源关联在一起的,包括服务端线程池、日志记录器、对象适配器等。
②对象适配器即Ice:
:
ObjectAdapter的创建:
一个通信器可以有一个或多个对象适器,一个对象适配器则可以有一个或多个Servant服务。
它把Ice对象映射到到来请求的servant,并把请求分派给每个servant中的应用代码(也就是说,对象适配器实现了一个向上调用接口,把Iceruntime与服务器中的应用代码连接在一起)。
③Servant服务到对象适配器的注册:
每一个实现了的Servant服务必须注册到对象适配器中才能生效,每个对象适配器都会维护一个叫做活动Servant表的数据结构,活动映射表用于把对象标识映射到servant中,当客户把操作调用发给服务器时,请求的目标是特定的传输端点。
传输端点隐含地标识了请求所针对的对象适配器(因为同一个端点只能绑定到一个对象适配器)。
客户端以发送请求的代理含有对应的对象的标识,客户端runtime会在线路上随调用一起发送这个对象标识。
对象适配器继而使
用这个对象标识、在它的ASM中查找正确的servant,把调用分派给它,如下图所示:
图2-1把请求绑定到正确的Servant
④对象适配器的激活:
使用其active操作,让适配器变成活动状态,一旦activate被调用,Iceruntime就会开始把请求分派给适配器的servants.
上述步骤用代码描述如下:
//ice通信器的建立
Ice:
:
CommunicatorPtric=Ice:
:
initialize();
//对象适配器的创建,需要指定适配器的名字、协议(缺省为TCP/IP)、IP地址和端口
Ice:
:
ObjectAdapterPtradapter
=ic->createObjectAdapterWithEndpoints("SimplePrinterAdapter","default-p10000");
//实例化一个RpcMethodI对象,为我们的RpcMethod接口创建一个servant
Ice:
:
ObjectPtrobject=newRpcMethodI;
//这里我调用适配器的add,告诉它有了一个新的servant;传给add的参数是我们刚才实例化的//servant,再加上一个标识符。
在这里,"SimpleRpcMethod1"串是servant的名字(如果我们有
//多个servant,每个servant都可以有不同的名字,更正确的说法是,都有不同的对象标识)。
adapter->add(object,ic->stringToIdentity("SimpleRpcMethod1"));
//调用适配器的activate方法激活适配器,适配器在创建时是处于扣留状态(holding),一旦适配器被
//激活,服务器就会开始处理来自客户的请求。
adapter->activate();
//这个操作挂起发出调用的线程,直到通信器关闭为止(也就是说,直到在服务器中不再有操作在执行为止)。
这//样,你可以在销毁通信器之前,等待服务器空闲下来
ic->waitForShutdown();
2.2.2服务器端类设计
(1)框架类:
这个类关键在于用slice语言定义好接口,然后用编译器生成对应的框架文件。
(2)服务实现类:
继承框架类中的抽象类,然后实现其纯虚方法,写具体的应用逻辑。
(3)服务端RpcServer类:
用ICE提供的API自己封装一个通用RpcServer类,使得各个模块能重用此代码。
(4)服务端RPC启动逻辑:
此部分使用RpcServer类建立好通信环境,并将
(2)中的服务实现类绑定到适配器,等待客户端请求到来。
附:
(RpcServer类设计)
#ifndefRPC_SERVER_H
#defineRPC_SERVER_H
#include
usingnamespacestd;
/************************************************************************/
/*用ICE提供的API自己封装一个通用RpcServer类,使得各个模块能重用此代码*/
/************************************************************************/
classRpcServer{
private:
//ICE通信器
Ice:
:
CommunicatorPtric;
//IC服务端的对象适配器
Ice:
:
ObjectAdapterPtradapter;
public:
//初始化RpcServer类,adapaterName表示对象适配器的名字,protocolSocketString是协议(缺省为TCP/IP)、IP地址和端口的串化表示形式
//返回表示初始化环境成功,返回-1表示失败
intinitializeRpcServer(stringadapaterName,stringprotocolSocketString){
try
{
//初始化通信器
this->ic=Ice:
:
initialize();
//创建对象适配器
this->adapter=this->ic->createObjectAdapterWithEndpoints(adapaterName,protocolSocketString);
}
catch(constIce:
:
SocketException&e){
cerr<cout<<"[异常:
RpcServer:
:
initializeRpcServer]出现SocketException,创建对象适配器失败!
"<return-1;
}
catch(constIce:
:
Exception&e){
cerr<cout<<"[异常:
RpcServer:
:
initializeRpcServer]抛出异常!
"<return-1;
}
return0;
}
//将具体的Servant服务绑定到对象适配器中,其中servantObject对象指针,而servantObjectName则指定servant在ASM表中的对象标识
//返回表示成功,返回-1表示失败
intbindServices(Ice:
:
ObjectPtrservantObject,stringservantObjectName){
try{
this->adapter->add(servantObject,ic->stringToIdentity(servantObjectName));
}catch(constIce:
:
AlreadyRegisteredException&e){
cerr<cout<:
bindServices]"<"<return-1;
}catch(constIceUtil:
:
NullHandleException&e){
cerr<cout<<"[异常:
RpcServer:
:
bindServices]使用了没有创建成功的对象适配器指针,抛出空异常!
"<return-1;
}catch(constIce:
:
Exception&e)
{
cerr<cout<<"[异常:
RpcServer:
:
bindServices]抛出异常!
"<return-1;
}
return0;
}
//将对象适配器激活,即启动server,这里服务器开始接收客户端请求,成功则返回,失败则返回-1
intstartServer(){
try{
this->adapter->activate();
this->ic->waitForShutdown();
}catch(constIceUtil:
:
NullHandleException&e){
cerr<cout<<"[异常:
RpcServer:
:
startServer]使用了没有创建成功的对象适配器指针,抛出空异常!
"<return-1;
}catch(constIce:
:
Exception&e){
cerr<cout<<"[异常:
RpcServer:
:
startServer]抛出异常!
"<return-1;
}
return0;
}
//销毁通信器,关闭server
voidstopServer(){
if(ic){
try{
ic->destroy();
}catch(constIce:
:
Exception&e){
cerr<cout<<"[异常:
RpcServer:
:
stopServer]抛出异常!
"<}
}
}
};
#endif
2.3客户端设计
2.3.1客户端功能设计
(1)客户端RPC通信的建立:
客户端RPC通信环境的建立分为如下几个步骤:
①客户端ICE通信器即Ice:
:
Communicator的建立:
通信器的一个实例是与客户器端一些运行资源关联在一起的,包括客户端线程池等。
②获取远地Servant的代理:
使用通信器的stringToProxy创建一个代理,所用的参数是“SimpleRpcMethod1:
default–h192.168.1.6–p10000”,这个串包含的是远地Servant的对象标识、通信协议(默认default为TCP/IP)、服务器IP地址和端口。
(2)客户端调用逻辑:
直接使用远地代理直接调用远程方法即可。
2.3.2客户端类设计
(1)存根类:
这个类和服务器端框架类是一样的,关键在于用slice语言定义好接口,然后用编译器生成对应的存根文件。
(2)客户端RpcClient类:
用ICE提供的API自己封装一个通用RpcClient类,使得各个模块能重用此代码。
(3)客户端调用逻辑:
先有RpcFactory类,其中对每个远地方法定义一个静态方法,如
staticvoidrpc_add_xml_string(stringxml,InternetAddressaddress){……..}
在应用逻辑上,直接使用RpcFactory:
:
rpc_add_xml_string(xml,address)即可。
附:
(RpcClient类设计)
#ifndef__RpcClient_h_
#define__RpcClient_h_
#include
usingnamespacestd;
/************************************************************************/
/*用ICE提供的API自己封装一个通用RpcClient类,使得各个模块能重用此代码*/
/************************************************************************/
classRpcClient{
private:
//ICE通信器
Ice:
:
CommunicatorPtric;
//客户端访问servant的代理总基类
Ice:
:
ObjectPrxbase;
public:
//初始化客户端ICE通信环境,成功返回,失败返回-1
intinitializeRpcClient(stringserverAddPro){
try{
//初始化通信器
this->ic=Ice:
:
initialize();
//根据提供的servant服务的端点及对象标识来获取客户端总代理
this->base=ic->stringToProxy(serverAddPro);
}catch(constIce:
:
Exception&ex){
cerr<cout<<"[异常:
RpcClient:
:
initializeRpcClient]:
抛出异常!
"<return-1;
}
return0;
}
//获得客户端总代理
Ice:
:
ObjectPrxgetBase(){
returnthis->base;
}
};
#endif
3、系统中RPC远程接口的设计与实现
3.1前端操作显示与后端调度服务软件之间接口
3.1.1传送完整计算拓扑(同步RPC)
接口基本描述:
接口名称
接口表示
接口简述
来源
目的地
传送计算
拓扑指令
cmTopologyAllRpc
前端操作显示软件向后端调度服务软件发送计算拓扑指令
前端操作显示软件
后端调度服务软件
接口详细表示:
voidcmTopologyAllRpc(stringtopologyAllXml)
输入参数:
stringtopologyAllXml:
前端软件生成的计算拓扑文件
返回参数:
无
3.1.2传送完整操作拓扑调度(同步RPC)
接口基本描述:
接口名称
接口表示
接口简述
来源
目的地
传送拓扑
调度指令
cmDeployAllRpc
前端操作显示软件向后端调度服务软件发送计算拓扑调度指令
前端操作显示软件
后端调度服务软件
接口详细表示:
voidcmDeployAllRpc(stringdeployAllXml)
输入参数:
stringdeployAllXml:
前端软件生成的拓扑调度指令
返回参数:
无
3.1.3获取全局操作拓扑(同步RPC)
接口基本描述:
接口名称
接口表示
接口简述
来源
目的地
获取全局
拓扑
cmGetTopologyRpc
前端操作显示软件向后端调度服务软件索取全局拓扑
前端操作显示软件
后端调度服务软件
接口详细表示:
stringcmGetTopologyRpc()
返回参数:
stringtopologyXml:
全局拓扑XML表示
3.1.4获取全局拓扑调度(同步RPC)
接口基本描述:
接口名称
接口表示
接口简述
来源
目的地
获取全局
拓扑调度
cmGetDeployRpc
前端操作显示软件向后端调度服务软件索取全局拓扑调度
前端操作显示软件
后端调度服务软件
接口详细表示:
stringcmGetDeployRpc()
返回参数:
stringdeployXml:
全局拓扑调度XML表示
上面的3.1.1-3.1.4操作封装到一个接口HeadServerRpc中,用Slice描述如下:
moduledemo{
interfaceHeadServerRpc{
voidcmTopologyAllRpc(stringtopologyAllXml);
voidcmDeployAllRpc(stringdeployAllXml);
stringcmGetTopologyRpc();
stringcmGetDeployRpc();
};
};
3.1.5刷新全局拓扑(同步RPC)
接口基本描述:
接口名称
接口表示
接口简述
来源
目的地
传送拓扑
调度指令
cmTopologyUpdateRpc
后端调度服务软件向前端操作显示软件发送计算拓扑更新指令
后端调度服务软件
前端操作显示软件
接口详细表示:
voidcmTopologyUpdateRpc(stringtopologyAllXml)
输入参数:
stringtopologyAllXml:
全局拓扑表示
返回参数:
无
3.1.6刷新全局拓扑调度(同步RPC)
接口基本描述:
接口名称
接口表示
接口简述
来源
目的地
传送拓扑
调度指令
cmDeployUpdateRpc
后端调度服务软件向前端操作显示软件发送拓扑调度更新指令
后端调度服务软件
前端操作显示软件
接口详细表示:
voidcmDeployUpdateRpc(stringdeployAllXml)
输入参数:
stringdeployAllXml:
全局拓扑调度表示
返回参数:
无
上面的3.1.5-3.1.6操作封装到一个接口RegionServerRpc中,用Slice描述如下:
moduledemo{
interfaceRegionServerRpc{
voidcmTopologyUpdateRpc(stringtopologyAllXml);
voidcmDeployUpdateRpc(stringdeployAllXml);
};
};
3.2后端调度服务软件与订阅管理软件之间接口
3.2.1订阅查询通知
接口基本描述:
订阅管理软件将新的查询条件发送给集群管理后端调度服务软件。
接口名称
接口表示
接口简述
来源
目的地
发送订阅
查询条件
cmSubscribeSql
订阅管理软件向后端调度服务软件发送订阅条件
订阅管理软件
后端调度服务软件
接口详细表示:
voidcmSubscribeSql(TypeEnumoperationType,stringreceiveAddressList,stringsubscribeSql,intflag)
输入参数:
TypeEnumoperationType,操作类型,ADD/DEL/MOD
stringreceiveAddressList,接收端的IP地址列表
Stringsql,订阅SQL
intflag主要用于标识update的SQL
返回参数:
无
上面的3.2.1操作封装到一个接口HeadServerRpc中,用Slice描述如下:
moduledemo