通过Bboss aop 1.0.6 新提供的远程服务调用功能,你可以实现:
Ø 点对点的远程服务调用(与ejb和rmi的功能一样,但是要简单得多)
Ø 多点远程服务调用(在集群环境中使用)
bboss项目下载列表 在sourceforge访问地址为:
https://sourceforge.net/project/showfiles.php?group_id=238653
体系结构
远程服务调用功能依赖于Bboss aop提供的以下组件来实现:
Ø 远程管理组件
Ø Aop组件管理组件
整个远程服务管理的体系结构如下:
远程服务调用过程
1. 客服端调用注册在aop框架中的业务组件的方法,给定特定的业务组件的serviceid
2. aop框架根据serviceid,判断调用的是远程方法还是本地方法
3. 如果serviceid 的值为 (all)/service或者(ip:port[;ip:port])/service模式则为远程方法调用,反之为service时则为本地业务方法调用。
4. aop框架识别的是本地服务调用时,会将调用请求直接转交给本地业务组件
5. aop框架识别的是远程服务调用时,会将调用请求直接转交给远程服务管理组件
6. 集远程服务管理组件接收到远程方法调用时,会将该请求递交给请求目标服务器中部署的aop框架中的业务组件,来完成请求的处理。
7. 处理完毕后将处理结果返回给客服端。
Bboss aop框架后续还会增加远程服务调用路由功能。
远程服务调用实例
环境准备
准备三台服务器
n 服务器A
Ip 172.16.17.56
端口 1185
n 服务器B
Ip 172.16.17.51
端口 1185
n 服务器C
Ip 172.16.17.52
端口 1185
服务部署
假设我们已经定义了业务组件test.A和test.B,分别实现接口test.ServiceInf。服务中定义了方法handle(),返回值为Object,方法代码如下:
public Object handle(){
return new Integer(1);
}
在A,B,C三台服务器上分别部署管理服务managerid,建立配置文件service-assemble.xml内容如下:
<manager-config>
<manager id="managerid" //管理服务id
singlable="true" //单列模式
>
<provider type="provider_a" //provider实现a
class="test.A" />
<provider type="provider_b" //provider实现b
class="test.B" />
<transactions>
<method name="handle" txtype="REQUIRED_TRANSACTION"/>
</transactions>
</manager>
<manager-config>
然后将service-assemble.xml导入到主配置文件manager-provider.xml文件:
<manager-config>
<managerimport file="com/chinacreator/spi/rpc/service-assemble.xml" />
</manager-config>
在主配置文件manager-provider.xml中添加以下配置,作为远程管理组件启用的控制开关:
<manager-config>
<properties>
<property name="cluster_enable" value="true"/> //启用远程管理组件
<property name="cluster_mbean_enable" value="true"/> //启用远程管理组件的mbean监控功能,目前未使用
<property name="cluster_name" value="Cluster"/> //远程管理组件名称
</properties>
<managerimport file="com/chinacreator/spi/rpc/service-assemble.xml" />
</manager-config>
配置服务器间通讯端口和组播地址
在远程管理组件的配置文件etc/META-INF/replSync-service-aop.xml
中修改bind_port属性:
bind_port="1185"
修改组播地址:
mcast_addr="228.10.10.178"
客服端程序编写
package com.chinacreator.spi.rpc;
import com.chinacreator.spi.BaseSPIManager;
import com.chinacreator.spi.SPIException;
public class Test {
public static void testMutirpcCall() {
try {
ServiceInf rpc = (test.ServiceInf)BaseSPIManager
.getProvider("(172.16.17.51:1185; 172.16.17.56:1185)/managerid");
// RPCTest rpc = (RPCTest)BaseSPIManager.getProvider("managerid");
// RPCTest rpc =
// (RPCTest)BaseSPIManager.getProvider("(_self)/managerid");
// RPCTest rpc1 = (RPCTest)BaseSPIManager.getProvider(
// "(172.16.17.52: 1185;172.16.17.56: 1185)/managerid ");
// RPCTest rpc2 =
// (RPCTest)BaseSPIManager.getProvider("(all)/managerid");
long s = System.currentTimeMillis();
Integer object = (Integer)rpc. handle();
System.out.println(BaseSPIManager.getRPCResult("172.16.17.51", "1185", object));
System.out.println(BaseSPIManager.getRPCResult("172.16.17.56", "1185", object));
long e = System.currentTimeMillis();
System.out.println((e - s) / 1000 + "秒");
} catch (SPIException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public static void testsinglerpcCall() {
try {
ServiceInf rpc = (test.ServiceInf)BaseSPIManager.getProvider("(172.16.17.56:1185)/managerid");
long s = System.currentTimeMillis();
Object object = rpc. handle();
System.out.println(object);
long e = System.currentTimeMillis();
System.out.println((e - s) / 1000 + "秒");
} catch (SPIException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public static void testAllrpcCall() {
try {
ServiceInf rpc = (test.ServiceInf)BaseSPIManager.getProvider("(all)/managerid");
long s = System.currentTimeMillis();
Object object = rpc .handle();
System.out.println(BaseSPIManager.getRPCResult("172.16.17.51", "1185", object));
System.out.println(BaseSPIManager.getRPCResult("172.16.17.52", "1185", object));
System.out.println(BaseSPIManager.getRPCResult("172.16.17.56", "1185", object));
long e = System.currentTimeMillis();
System.out.println((e - s) / 1000 + "秒");
} catch (SPIException e) {
e.printStackTrace();
}
}
}
启动服务器
分别在每台服务器上运行类com.chinacreator.remote.RunAop的主方法,既可以启动服务器。
注意,客服端不要启动服务器。
运行客服端
Test.testAllrpcCall();
Test.testMutirpcCall();
Test.testsinglerpcCall();
更详细的信息请参考章节【BaseSPIProvider组件介绍中.实现远程服务调用】
网络环境
网络的要求就是要求bboss aop中的远程管理组件能够在各个服务器之间进行通讯,根据采用的不同网络协议,说明如下:
Udp协议:确保能够进行udp通讯,如果你的机器之间装有防火墙之类的软件,必须允许udp数据包的传递。
Tcp协议:确保能够进行tcp通讯,如果你的机器之间装有防火墙之类的软件,必须允许tcp数据包的传递。
另外,为了确保远程服务调用能够正确执行,还要求远程服务方法的返回值和参数必须实现java.io.Serializable接口。
点对点远程服务调用和组播服务调用的区别
点对点远程服务调用和组播服务调用的区别主要在于
1. 方法有返回值时其返回值不同。
在实例中我们看到组件方法:
public Object handle(){
return new Integer(1);
}
的返回值类型是Object,实际上返回的是一个Integer类型的对象。在点对点远程服务调用的测试方法中,我们直接将服务方法的返回值直接转换为Integer类型:
ServiceInf rpc = (test.ServiceInf)BaseSPIManager
.getProvider("(172.16.17.51:1185; 172.16.17.56:1185)/managerid");
Integer object = (Integer)rpc. handle();
但是在组播调用远程服务的测试方法中,不能这样处理,原因是发出请求的每台服务器都会有一个返回值,因此rpc. handle()调用的结果将是一个返回值的集合,如果需要获取特定服务器的返回值,必须通过以下方法来获取:
BaseSPIManager.getRPCResult(serverip, port, object);
Serverip参数对应服务器的ip,port参数对应服务器的端口,object参数为所有服务器的返回值的集合。
例如:
ServiceInf rpc = (test.ServiceInf)BaseSPIManager.getProvider("(172.16.17.51:1185; 172.16.17.56:1185)/managerid");
Object object = rpc .handle();
Integer value = (Integer)BaseSPIManager.getRPCResult("172.16.17.56", "1185", object);
Integer value1 = (Integer)BaseSPIManager.getRPCResult("172.16.17.51", "1185", object);
2. 配置远程组件时,对组播地址的配置要求不一样
在远程管理组件的配置文件etc/META-INF/replSync-service-aop.xml,我们需要配置两个属性:
组播地址 mcast_addr="228.10.10.178"
绑定端口 bind_port="1185"
每个服务器都可以配置自己的组播地址和绑定端口,如果两台服务器之间发出的所有远程服务请求都是点对点的方式发出的,那么组播地址 mcast_addr就可以配置成不相同的地址,当然相同的地址也可以(尽量不要配置成相同的组播地址);如果服务器之间发出的远程服务请求只要有同时发出对多台服务器调用的情况时,就需要将组播地址mcast_addr配置成相同的地址。
远程服务id定义规则
Bboss aop框架的业务组件既可以作为本地服务调用,又可以作为远程服务调用,那么怎么进行远程调用和本地调用呢,本小节就详细的进行说明。
我们进行服务调用时,首先要将提供服务的业务组件配置到bboss的部署描述文件中,这样就可以通过BaseSPIManager组件提供的getProvider方法获取相应组件的实例,然后进行方法调用了。例如:
A) ServiceInf rpc = (test.ServiceInf)
BaseSPIManager.getProvider("(172.16.17.56:1185)/managerid");
B) ServiceInf local = (test.ServiceInf)
BaseSPIManager.getProvider("managerid");
很明显情况A)获取的是主机172.16.17.56:1185上的服务实例,而B)则获取的是本地的服务实例。
从上面的示例我们能够看出,远程服务和本地服务的调用主要是通过getProvider方法的第一个参数serviceid来决定的。那么这个serviceid的定义规则到底是怎样的呢,下面来详细说明。
本地服务id
本地服务id的规则很简单,就是部署描述文件中的配置的maanger 节点的id属性。例如:
<manager id="managerid" //管理服务id
singlable="true" //单列模式
>
<provider type="provider_a" //provider实现a
class="test.A" />
</manager>
本地服务id就是managerid。
远程服务id
远程服务id的规则如下:
(all|_self|ip:port[;ip:port])/serviceid
说明:
Ø (all) /serviceid
在集群中的所有服务器节点中调用远程服务serviceid。服务方法返回值必须是Object类型,如果要获取相应服务器的返回值,可以用以下的方式获取:
Object retvalue = rpc .handle();
Integer value = (Integer)BaseSPIManager.getRPCResult("172.16.17.56", "1185", retvalue);
Ø (ip:port) /serviceid
单点调用远程服务serviceid。服务方法返回值可以是具体的类型,调用方法如下:
Integer value = (Integer) = rpc .handle();
这种模式需要说明的是,如果ip和port就是本地的ip地址和port,那么bboss aop就会将本次调用作为本地服务调用。
Ø (ip:port;ip1:port) /serviceid
在集群中的指定的几个服务器节点中调用远程服务serviceid。服务方法返回值必须是Object类型,如果要获取相应服务器的返回值,可以用以下的方式获取:
Object retvalue = rpc .handle();
Integer value = (Integer)BaseSPIManager.getRPCResult("172.16.17.56", "1185", retvalue);
Ø (_self) /serviceid
相当于本地服务调用。
异常的处理
所有的远程调用的过程中抛出的异常,都会作为
com.chinacreator.remote.RemoteException类型的异常抛出。