RMI、Hessian、Burlap、Httpinvoker、WebService的比较

RMI、Hessian、Burlap、Httpinvoker、WebService的比较 2(转)

   

【2】Java远程调用方法性能比较
 【IT168技术】如今,Java远程调用方法不少,各类方法的优缺点网络上也有不少的参考文章,此次我对几个典型的Java远程调用方法作了一个简单的性能分析比较,可供你们参考。
  测试环境
  CPU:奔腾双核 T4500,内存:DDR3-1067 2G,Web容器:Tomcat6.0.33,操做系统:WinXP-sp3
  测试项目
  ①RMI:用Spring3集成发布。
  ②hessian:用Spring3集成发布到Tomcat容器。
  ③Java6 WebService:使用Java6原生WebService和注解,直接用Endpoint.publish发布服务。
  ④CXF:用Spring3集成发布到Tomcat容器。
  测试结果java

 


  说明:以上测试虽不是很是的精确,但基本能说明必定的问题。每项案例的服务端方法,都是简单方法,接收客户端传递的一个String类型参数,并打印 到console。每轮测试取三次时间的平均值。全部单线程访问测试所有完成并正常处理请求,没有请求拒绝状况发生。而并发访问测试,除hessian中 途抛异常没法完成,其他均正常完成测试。
  结论:
  RMI的性能最高,这已经是公认,RMI底层基于Java远程方法协议(JRMP)和对象序列化技术,而JRMP是直接基于TCP/IP协议的封装,在 网络上传输2 byte的有效数据,对于TCP而言,总共有478 byte被额外传输,而对于RMI, 1645byte被额外传输。可见RMI的效率仍是至关不错的。JavaEE标准的EJB就是基于RMI的调用。
  hessian是一个轻量级的remoting on http框架。Hessian没有采用WebService标准的SOAP协议,而是本身实现了一种二进制RPC(Remote Procedure Call Protocol,远程过程调用协议)协议。Hessian的设计理念就是简单高效,使用 Hessian 传输数据量比Soap协议要小不少。这也是为何Hessian的性能要高于WebService。可是,尽管它再简单高效,它始终是基于Http协议上 封装的,因此仍是比Java的RMI效率要差一些。我看过其余的一些研究测试报告称Hessian在传输少许对象时,比RMI还要快速高效,但传输数据结 构复杂的对象或大量数据对象时,较RMI要慢20%左右。这个结论在个人测试中还真没有发现,也许与测试环境或方法有关系吧。
  Java6WebService 和 CXF的性能应该说是基本同级别,前者略高于后者。众所周知WebService是基于Soap协议实现的,而Soap协议是在Http协议基础上的 XML定义和封装。全部的请求和响应都要被转化成符合SOAP标准的XML格式。显然这直接会致使效率的下降。
  XML格式的协议是一种易读易理解的协议,但并不高效。解析和组装XML协议数据流都须要耗费系统的处理时间,因此,WebService的性能不如 Hessian。这里要说一下的是Java6原生的WebService,开发起来很是方便,并且无需额外引入一大堆的Jar包。性能又强于CXF,至于 Axis2和Axis1就更不用说,已经有不少测试代表CXF的性能是Axis2的2倍以上,是Axis1的2-6倍。
  那么既然RMI性能那么好,为何咱们须要那么多其余的远程调用方式呢?这个问题又引起到了一个原始的真理。越原始越底层的技术效率就越高,但局限性 也就越大。RMI是Java的特性,那么它必须基于JVM运行,也就是说RMI没法跨语言运行。而WebService就不一样了,Soap是基于标准 Http协议的,是一种与语言无关的字符级协议,因此它能够更好的实现异构系统的通讯。这在咱们现实环境中是很是有用的,相信你们仍是 WebService用的比较多点吧。
不足:此次的测试,仍是存在不少不足之处。
【3】远程调用服务池或者服务工厂
在现代 J2EE 企业应用系统中,存在着 Hessian 、 HttpInvoker 、 XFire 、 Axis 等多种形式的远程调用技术。尽管有 Spring 等框架对这些技术进行了封装,下降了使用的复杂度,但对普通程序员而言还是复杂的——至少须要要掌握这些技术的基础知识。
不管使用那种技术,其基本原理都是同样的:服务端生成骨架,对外暴露服务;客户端生成服务代理,访问调用服务。一般状况下,生成服务代理的代价比较高昂, 这也是咱们第一次访问远程服务速度比较慢的缘由,为每一个请求生成新的服务代理恐怕不是咱们所指望的。更况且,若是采用这种方式,就要在代码里针对各类不一样 的技术(如 XFire 、 HttpInvoker )编写不一样的服务生成和调用的处理代码。不只麻烦,并且容易出错。我想,没有人愿意去直接操做各类框架技术的底层代码,这并非一个好注意!程序员

做为一种替代方案,咱们设计了一个“服务池”的功能,或者说“服务工厂”更贴切一点。先看下面这张类图:服务器


如上图所示,针对 HttpInvoker 、 XFire 、 Hessian 等各类远程调用技术,抽象出一个“远程服务池”(服务工厂)既 RemoteServicePool 接口。该接口提供了获取服务及一些其余的辅助功能,并针对 HttpInvoker 、 XFire 、 Hessian 等不一样技术提供了相应的具体实现。采用这种方式,开发人员只需在代码中“注入” RemoteServicePool ,并以统一的方式(如 getService() )获取实际的服务,只是针对不一样技术在配置上有些须差别而已。该技术的原理很是简单,在应用启动以前把全部存在的服务提供者提供的服务都配置好,并为它们 分配一个惟一的 ID 。应用启动以后,框架会自动生成和这些地址相对应的服务代理( ServiceProxy ),这些代理已是可用的服务,服务获取的细节被彻底屏蔽掉,开发者只要知道如何从 RemoteServicePool 中获取服务就能够了。看一下服务池的接口定义:
java 代码
1.
9.
10. public interface RemoteServicePool {
11.
12.
26.
27. Object getService(String serviceId);
28.
29. }网络

xml 代码
1. <bean id="userServicePool" class="com. tonysoft .common.XFireRemoteServicePool">
2. <property name="serviceInterface">
3. <value>com. tonysoft .demo.service.UserServicevalue>
4. property>
5. <property name="serviceUrls">
6. <map>
7. <entry key=" server 1 ">
8. <value>http://localhost:8080/server1/service/userService?WSDLvalue>
9. entry>
10. <entry key="server2">
11. <value>http://localhost:8080/server2/service/userService?WSDLvalue>
12. entry>
13. map> J2EE 企业应用系统中,存在着 Hessian 、 HttpInvoker 、 XFire 、 Axis 等多种形式的远程调用技术。尽管有 Spring 等框架对这些技术进行了封装,下降了使用的复杂度,但对普通程序员而言还是复杂的——至少须要要掌握这些技术的基础知识。
14. property> 接下来看看如何配置服务:
15. bean>
最后再来看一下访问服务的代码:
java 代码
1.
2. public RemoteServicePool userServicePool ;
3.
6.
7. public void testAddUser() {
8.
9. UserService userService = null ;
10. try {
11. userService =(UserService) userServicePool .getService("server2");
12. } catch (Exception e){
13. throw new RuntimeException( " 获取服务失败,失败缘由:" + e);
14. }
15.
16. OperateResult result = userService .addUser( new User( "daodao" , " 技术部" ));
17.
18. assertEquals(result.isSuccess(), true );
19.
20. }
该方案还为“双向关联”的系统服务提供了一个很好解决办法。看下面一张图:并发

如图,系统 B 和系统 C 都调用系统 A 进行付款操做;同时系统 A 要用远程服务向系统 B 或系统 C 进行认证操做,认证操做的接口(契约)都是同样的,业务逻辑可能有所差别。在这种状况下,配置在系统 A 中的认证服务就比较麻烦,由于要根据不一样的系统调用认证服务,既从 B 过来的请求要访问 B 的认证服务,从 C 过来的请求要访问 C 的认证服务。用服务池能够很好的解决这个问题,把两个系统( B 、 C )提供的认证服务地址都配置在同一个服务池中,根据不一样的 ID (如 B 、 C )来决定使用那个系统的服务。
ServiceResportApplication.rar
描述:
下载
文件名: ServiceResportApplication.rar
文件大小: 49 KB
下载过的: 文件被下载或查看 104 次
尽管服务池解决了一些问题,在某种程度上下降了复杂度,但仍存在以下一些问题:
服务的运行期动态注册
服务的自动注入( IoC )
透明化服务 ID 的传递框架

在服务池( ServicePool )概念的基础上进行扩展,咱们得出了以下的系统模型:ide

 

在核心位置上是一个服务中心资源库( ServiceRepository ),存储了系统中用到的全部的远程服务。服务采起动态注册的机制,由对外提供的服务注册器( ServiceRegister )提供服务注册功能。外部系统能够实现该接口向资源中心注册服务。提供了一个启动时运行的注册器,能够把静态配置在系统中的服务都注册进来。性能

服务的生成、管理等均由服务中心本身维护,委托服务代理生成器( ServiceProxyGenerator )完成服务的建立。能够针对现有的远程调用方式,如 XFire,HttpInvoker,Hessian 等建立服务代理,也能够针对本身定义的远程调用方式建立服务代理,由 CustomServiceProxyGenerator 完成该功能。
一个服务模型包括 5 个因素:
服务接口 serviceClass
服务 ID serviceId
服务类型 serviceType
服务地址 serviceUrl
附加属性 props
查找一个服务须要两个因素,一个是服务接口,另外一个是服务 ID 。这两个因素共同决定了一个服务,既服务中心内部的“服务 ID ”。经过这种方式,能够容许存在多个 ID 相同但接口不一样的服务,也能够存在多个接口相同但 ID 不一样的服务。单元测试

服务 ID 的获取是系统中一个关键的功能,这部分对程序员来讲应该是透明的,由系统本身维护。相应的提供了一个服务 ID 提供者 (ServiceIdProvider) 接口,由实现该接口的子类完成服务 ID 获取功能(这是比较关键的地方,须要特殊考虑)。测试

对于程序员来讲,使用服务中内心的服务不再能比这样再简单了!看看配置:
xml 代码

1. < bean id = "helloHttpInvokerService" parent = "abstractServiceProxyFactory" >
2. < property name = "serviceInterface" >
3. < value > com.tonysoft.common.service.repository.example.HelloHttpInvoker value >
4. property >
5. bean >
再看如何使用这个 bean :
1. private HelloHttpInvoker helloHttpInvokerService ;
2. public void testHttpInvoker() {
3. assertNotNull( "helloHttpInvokerService can't be null !" , helloHttpInvokerService );
4. assertEquals ( "Hello , HttpInvoker !" , helloHttpInvokerService .sayHello());
5. }
6.
10.
11. public void setHelloHttpInvokerService(HelloHttpInvoker helloHttpInvokerService) {
12. this . helloHttpInvokerService = helloHttpInvokerService;
13. }

就是这样的简单! Spring 会把这个 bean 自动注入到程序中,能够象使用其余任何 bean 同样使用它!程序员彻底不用关心该服务由谁提供、采用什么技术,他只要知道系统中存在这样一个服务就 OK 了。该技术完全向程序员屏蔽了底层技术的实现细节,以统一的方式访问任何形式的远程服务。至于服务是如何生成、如何配置的将在后面叙述。

服务( Service Bean )是如何实现自动注入( IoC )的呢?
注意到上面配置的 bean 都继承了“ abstractServiceProxyFactory ”,它是一个工厂 bean ,负责根据给定的接口类型,到服务中心( ServiceRepository )查找服务,并生成服务代理。咱们来看一下它的核心代码:
java 代码


1.
15. public class ServiceProxyFactory implements FactoryBean {
16.
17.
18. private ServiceRepository serviceRepository ;
19.
20.
21. private ServiceIdProvider serviceIdProvider ;
22.
23. private Class serviceInterface ;
24.
27. public Object getObject() throws Exception {
28. return ProxyFactory.getProxy(getObjectType(), new ServiceProxyInterceptor());
29. // return serviceRepository.getService(serviceInterface, serviceIdProvider.getCurrentServiceId());
30. }
31.
34. public Class getObjectType() {
35. return serviceInterface ;
36. }
37.
40. public boolean isSingleton() {
41. return true ;
42. }
43.
46. private class ServiceProxyInterceptor implements MethodInterceptor {
47.
50. public Object invoke(MethodInvocation invocation) throws Throwable {
51. Method method = invocation.getMethod();
52. Object[] args = invocation.getArguments();
53. Object client = getClient();
54. return method.invoke(client, args);
55. }
56. private Object getClient() {
57. try {
58. return serviceRepository .getService( serviceInterface , serviceIdProvider .getCurrentServiceId());
59. } catch (ServiceException e) {
60. // TODO
61. e.printStackTrace();
62. return null ;
63. }
64. }
65. }
66. // ---- 容器自动注入 ----
67. ••••••

真正的魅力就在这个地方。根据服务接口类型和服务 ID ,从服务中心获取特定的服务。服务接口是配置好的, 而服务 ID 则在运行时才能肯定,根据不一样的应用、不一样的策略提供不一样的 ServiceIdProvider 。其中用到了 Spring 的 FactoryBean 和拦截器,至于为何要在这里使用拦截器,能够参考 Spring 框架的源码。

服务代理生成器( ServiceProxyGenerator )也是一个值得一提的地方,咱们先看一下它的接口:

1.
6. public interface ServiceProxyGenerator {
7.
8.
20. Object getService(Class serviceClass, String serviceUrl, Properties props) throws Exception;
21. }
22.
23.
24. public void registService(ServiceModel serviceModel) throws ServiceException {
25. ••••••
26. String key = serviceModel.getServiceId() + KEY_SPAR
27. + serviceModel.getServiceClass().getName();
28. if ( serviceNames .contains(key)) {
29. throw new ServiceRegistException( "service is exist!" );
30. }
31. Object proxy = null ;
32. try {
33. ServiceProxyGenerator proxyGenerator = (ServiceProxyGenerator) beanFactory
34. .getBean(serviceModel.getServiceType() + PROXY_GENERATOR_END );
35. proxy = proxyGenerator.getService(serviceModel.getServiceClass(), serviceModel
36. .getServiceUrl(), serviceModel.getProps());
37. } catch (Exception e) {
38. throw new ServiceRegistException( "can't regist service !" , e);
39. }
40. if (proxy != null ) {
41. serviceNames .add(key);
42. serviceContainer .put(key, proxy);
43. } else {
44. throw new ServiceRegistException( "fail to regist service !" );
45. }
46. }


上面作特殊标记的代码就是应用服务代理生成器的地方,这里咱们用到了 Spring 的 bean 工厂,根据注册服务的类型( xfire,httpinvoker,hessian 等)到 Spring 容器里查找相应的生成器,并生成指定类型的服务。看下面配置的几个服务代理生成器:
xml 代码

1. <!-- XFire 类型服务代理生成器 -->
2. < bean id = "xfire_generator" class = "com.tonysoft.common.service.repository.generator.XFireServiceProxyGenerator" lazy-init = "true" >
3. < property name = "serviceFactory" >
4. < ref bean = "xfire.serviceFactory" />
5. property >
6. bean >
7.
8. <!-- Hessian 类型服务代理生成器 -->
9. < bean id = "hessian_generator" class = "com.tonysoft.common.service.repository.generator.HessianServiceProxyGenerator" lazy-init = "true" >
10. bean >
11.
12. <!-- HttpInvoker 类型服务代理生成器 -->
13. < bean id = "httpinvoker_generator" class = "com.tonysoft.common.service.repository.generator.HttpInvokeServiceProxyGenerator" lazy-init = "true" >
14. bean >
15.
16. <!-- 自定义 类型服务代理生成器 -->
17. < bean id = "custom_generator" class = "com.tonysoft.common.service.repository.generator.CustomServiceProxyGenerator" lazy-init = "true" >
18. bean >
19.
20. <!-- 服务中心(资源库) -->
21. < bean id = "serviceRepository" class = "com.tonysoft.common.service.repository.DefaultServiceRepository" >
22. bean >
23.
24. <!-- 服务 ID 提供者 -->
25. < bean id = "serviceIdProvider" class = "com.tonysoft.common.service.repository.provider.DefaultServiceIdProvider" >
26. bean >
27. <!-- 全部远程服务的基础类 -->
28. < bean id = "abstractServiceProxyFactory" class = "com.tonysoft.common.service.repository.ServiceProxyFactory" abstract = "true" >
29. bean >

简单看一下 HttpInvoker 类型服务代理生成器的代码:
java 代码

1. public class HttpInvokeServiceProxyGenerator implements ServiceProxyGenerator {
2.
3.
4. private HttpInvokerProxyFactoryBean httpInvokerFactory = new HttpInvokerProxyFactoryBean();
5.
9. public Object getService(Class serviceClass, String serviceUrl, Properties props) {
10. // Todo initial httpInvokerFactory with props
11. httpInvokerFactory .setServiceInterface(serviceClass);
12.
13. httpInvokerFactory .setServiceUrl(serviceUrl);
14.
15. // must invoke this method
16. httpInvokerFactory .afterPropertiesSet();
17.
18. return httpInvokerFactory .getObject();
19.
20. }
21.
22. }
是的,正如你所看到的同样,咱们这里把真正生成服务代理的任务交给了 Spring 的 HttpInvokerProxyFactoryBean 来完成。

提供在初始化时注册的静态服务功能,配制以下:
1. <!-- 初始化时注册的静态服务 -->
2. < bean id = "bootupServiceRegister" class = "com.tonysoft.common.service.repository.register.BootupServiceRegister" lazy-init = "false" >
3. < property name = "services" >
4. < list >
5. < bean class = "com.tonysoft.common.service.repository.ServiceModel" >
6. < property name = "serviceClass" >< value > com.tonysoft.common.service.repository.example.HelloHttpInvoker value > property >
7. < property name = "serviceId" >< value > default value > property >
8. < property name = "serviceType" >< value > httpinvoker value > property >
9. < property name = "serviceUrl" >< value >http://localhost:8080/serviceRepositoryApplication...voker/helloHttpInvoker.service value > property >
10. < property name = "props" >
11. < props > props >
12. property >
13. bean >
14. < bean class = "com.tonysoft.common.service.repository.ServiceModel" >
15. < property name = "serviceClass" >< value > com.tonysoft.common.service.repository.example.HelloXFire value > property >
16. < property name = "serviceId" >< value > default value > property >
17. < property name = "serviceType" >< value > xfire value > property >
18. < property name = "serviceUrl" >< value >http://localhost:8080/serviceRepositoryApplication.../xfire/helloXFire.service?WSDL value > property >
19. < property name = "props" >
20. < props > props >
21. property >
22. bean >
23. list >
24. property >
25. bean >
具体内容能够参看附件中的资源:
1、 ServiceRepository 的源代码( Eclipse 工程)
2、 一个示例应用
3、 打包部署的 ANT 脚本

把项目导入 Eclipse 中,直接运行 Ant 脚本,在 target 目录下会生成服务中心的 jar 包,同时生成示例应用的 war 包,把 war 包放到任意服务器( Server )上并启动服务器并确保应用正常启动。 运行 ServiceRepositoryTest .java 执行完整的单元测试,观测结果。其余的本身看源码吧。

相关文章
相关标签/搜索