读书人

Spring HttpInvoke的实现(远程接口调用

发布时间: 2012-06-30 17:20:12 作者: rapoo

Spring HttpInvoke的实现(远程接口调用),以及效率提升!解决Read timed out问题

最近接手服务器总被人质疑效率问题,说到底是质疑Spring HttpInvoke的效率问题。好在经过我的努力,找到了问题的根源,最终解决了这个问题。?
我也顺道整理一下Spring HttpInvoke——那曾经最为熟悉的东西。?
Spring HttpInvoke,一种较为常用的、基于Spring架构的服务器之间的远程调用实现,可以说是轻量级的RMI。?
最初,我们使用Spring HttpInvoke同步配置数据,刷新多个服务器上的缓存,当然如果用分布式缓存是不是更好Spring HttpInvoke的实现(远程接口调用),以及效率提升!解决Read timed out有关问题?!?
使用Spring HttpInvoke,你可以调用远程接口,进行数据交互、业务逻辑操作等等。?
废话不说了,上代码!?
用户操作接口:?

Java代码?Spring HttpInvoke的实现(远程接口调用),以及效率提升!解决Read timed out有关问题?Spring HttpInvoke的实现(远程接口调用),以及效率提升!解决Read timed out有关问题
  1. /**?
  2. ?*?@author?<a?href="mailto:zlex.dongliang@gmail.com">梁栋</a>?
  3. ?*?@since?1.0?
  4. ?*/??
  5. public?interface?UserService?{??
  6. ??
  7. ????/**?
  8. ?????*?获得用户?
  9. ?????*??
  10. ?????*?@param?username?
  11. ?????*????????????用户名?
  12. ?????*?@return?
  13. ?????*/??
  14. ????User?getUser(String?username);??
  15. }??

用户类,注意实现Serializable接口,这是执行远程调用传递数据对象的第一要求——数据对象必须实现Serializable接口,因为,要执行序列化/反序列化操作!?
Java代码?Spring HttpInvoke的实现(远程接口调用),以及效率提升!解决Read timed out有关问题?Spring HttpInvoke的实现(远程接口调用),以及效率提升!解决Read timed out有关问题
  1. /**?
  2. ?*?@author?<a?href="mailto:zlex.dongliang@gmail.com">梁栋</a>?
  3. ?*?@since?1.0?
  4. ?*/??
  5. public?class?User?implements?Serializable?{??
  6. ??
  7. ????private?static?final?long?serialVersionUID?=?5590768569302443813L;??
  8. ????private?String?username;??
  9. ????private?Date?birthday;??
  10. ??
  11. ????/**?
  12. ?????*?@param?username?
  13. ?????*?@param?birthday?
  14. ?????*/??
  15. ????public?User(String?username,?Date?birthday)?{??
  16. ????????this.username?=?username;??
  17. ????????this.birthday?=?birthday;??
  18. ????}??
  19. ???????//?省略??
  20. ????/*?
  21. ?????*?(non-Javadoc)?
  22. ?????*??
  23. ?????*?@see?java.lang.Object#toString()?
  24. ?????*/??
  25. ????@Override??
  26. ????public?String?toString()?{??
  27. ????????return?String.format("%s\t%s\t",?username,?birthday);??
  28. ????}??
  29. }??

覆盖toString()方法,输出用户信息!?
再看UserServiceImpl实现:?
Java代码?Spring HttpInvoke的实现(远程接口调用),以及效率提升!解决Read timed out有关问题?Spring HttpInvoke的实现(远程接口调用),以及效率提升!解决Read timed out有关问题
  1. /**?
  2. ?*?@author?<a?href="mailto:zlex.dongliang@gmail.com">梁栋</a>?
  3. ?*?@since?1.0?
  4. ?*/??
  5. public?class?UserServiceImpl?implements?UserService?{??
  6. ????private?Logger?logger?=?Logger.getLogger(UserServiceImpl.class);??
  7. ??
  8. ????/*?
  9. ?????*?(non-Javadoc)?
  10. ?????*??
  11. ?????*?@see?
  12. ?????*?org.zlex.spring.httpinvoke.service.UserService#getUser(java.lang.String)?
  13. ?????*/??
  14. ????@Override??
  15. ????public?User?getUser(String?username)?{??
  16. ????????if?(logger.isDebugEnabled())?{??
  17. ????????????logger.debug("username:["?+?username?+?"]");??
  18. ????????}??
  19. ????????User?user?=?new?User(username,?new?Date());??
  20. ????????if?(logger.isDebugEnabled())?{??
  21. ????????????logger.debug("user:["?+?user?+?"]");??
  22. ????????}??
  23. ????????return?user;??
  24. ????}??
  25. ??
  26. }??

只把用户信息打出来即可说明调用效果!?
看applicationContext.xml?
Xml代码?Spring HttpInvoke的实现(远程接口调用),以及效率提升!解决Read timed out有关问题?Spring HttpInvoke的实现(远程接口调用),以及效率提升!解决Read timed out有关问题
  1. <bean??
  2. ????id="userService"??
  3. class="org.springframework.remoting.httpinvoker.HttpInvokerServiceExporter">??
  4. ????<property??
  5. ????????name="service">??
  6. ????????<bean??
  7. class="org.zlex.spring.httpinvoke.service.impl.UserServiceImpl"?/>??
  8. ????</property>??
  9. ????<property??
  10. ????????name="serviceInterface"??
  11. ????????value="org.zlex.spring.httpinvoke.service.UserService"?/>??
  12. </bean>??

我们要把userService暴露出去,这样外部就可以通过http接口调用这个接口的实现了。?
说说HttpInvokerServiceExporter,这个类用来在服务器端包装需要暴露的接口。?
熟悉service,定义具体的实现类!?
Xml代码?Spring HttpInvoke的实现(远程接口调用),以及效率提升!解决Read timed out有关问题?Spring HttpInvoke的实现(远程接口调用),以及效率提升!解决Read timed out有关问题
  1. <property??
  2. ????name="service">??
  3. ????<bean??
  4. lass="org.zlex.spring.httpinvoke.service.impl.UserServiceImpl"?/>??
  5. </property>??

熟悉serviceInterface指向需要暴露的接口,注意使用value标注接口名称!?
Xml代码?Spring HttpInvoke的实现(远程接口调用),以及效率提升!解决Read timed out有关问题?Spring HttpInvoke的实现(远程接口调用),以及效率提升!解决Read timed out有关问题
  1. <property??
  2. ????name="serviceInterface"??
  3. ????value="org.zlex.spring.httpinvoke.service.UserService"?/>??

最后再看servlet.xml配置?
Xml代码?Spring HttpInvoke的实现(远程接口调用),以及效率提升!解决Read timed out有关问题?Spring HttpInvoke的实现(远程接口调用),以及效率提升!解决Read timed out有关问题
  1. <bean?class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">??
  2. ????<property??
  3. ????????name="urlMap">??
  4. ????????<map>??
  5. ????????????<entry??
  6. ????????????????key="*"??
  7. ????????????????value-ref="userService"?/>??
  8. ????????</map>??
  9. ????</property>??
  10. </bean>??

直接将请求指向刚才配置的userService?
现在我们之间访问一下http://localhost:8080/spring/service/?
Spring HttpInvoke的实现(远程接口调用),以及效率提升!解决Read timed out有关问题
这就说明,服务器端配置已经成功了!如果在日志中频繁得到这种异常,那很可能服务器被恶意访问了!?
再看客户端实现:?
Java代码?Spring HttpInvoke的实现(远程接口调用),以及效率提升!解决Read timed out有关问题?Spring HttpInvoke的实现(远程接口调用),以及效率提升!解决Read timed out有关问题
  1. /**?
  2. ?*?@author?<a?href="mailto:zlex.dongliang@gmail.com">梁栋</a>?
  3. ?*?@since?1.0?
  4. ?*/??
  5. public?class?UserServiceTest?{??
  6. ????private?Logger?logger?=?Logger.getLogger(UserServiceTest.class);??
  7. ????private?ApplicationContext?context;??
  8. ??
  9. ????private?UserService?userService;??
  10. ??
  11. ????@Before??
  12. ????public?void?initialize()?{??
  13. ????????context?=?new?ClassPathXmlApplicationContext("applicationContext.xml");??
  14. ????????userService?=?(UserService)?context.getBean("userService");??
  15. ????}??
  16. ??
  17. ????@Test??
  18. ????public?void?getUser()?{??
  19. ????????User?user?=?userService.getUser("zlex");??
  20. ????????if?(logger.isDebugEnabled())?{??
  21. ????????????logger.debug("user["?+?user?+?"]");??
  22. ????????}??
  23. ????}??
  24. }??

我们做了什么?Nothing!就跟调用一般Spring容器中的实现一样!?
再看applicationContext.xml:?
Xml代码?Spring HttpInvoke的实现(远程接口调用),以及效率提升!解决Read timed out有关问题?Spring HttpInvoke的实现(远程接口调用),以及效率提升!解决Read timed out有关问题
  1. <bean??
  2. ????id="userService"??
  3. class="org.springframework.remoting.httpinvoker.HttpInvokerProxyFactoryBean">??
  4. ????<property??
  5. ????????name="serviceUrl"??
  6. ????????value="http://localhost:8080/spring/service"?/>??
  7. ????<property??
  8. ????????name="serviceInterface"??
  9. ????????value="org.zlex.spring.httpinvoke.service.UserService"?/>??
  10. </bean>??

这里我们可以通过Spring容器调用userService,而实际上,他是一个HttpInvokerProxyFactoryBean,在这个配置里,定义了访问地址serviceUrl,和访问接口serviceInterface。?
执行测试!?
Spring HttpInvoke的实现(远程接口调用),以及效率提升!解决Read timed out有关问题
如果我们这样写,其实默认调用了SimpleHttpInvokerRequestExecutor做实现,这个实现恐怕只能作为演示来用!?
这也是效率问题所在!!!?
为提高效率,应该通过Commons-HttpClient!?
我们需要做什么?导入这个jar,改改xml就行!?
Xml代码?Spring HttpInvoke的实现(远程接口调用),以及效率提升!解决Read timed out有关问题?Spring HttpInvoke的实现(远程接口调用),以及效率提升!解决Read timed out有关问题
  1. <bean??
  2. ????id="userService"??
  3. ????class="org.springframework.remoting.httpinvoker.HttpInvokerProxyFactoryBean">??
  4. ????<property??
  5. ????????name="serviceUrl"??
  6. ????????value="http://localhost:8080/spring/service"?/>??
  7. ????<property??
  8. ????????name="serviceInterface"??
  9. ????????value="org.zlex.spring.httpinvoke.service.UserService"?/>??
  10. ????<property??
  11. ????????name="httpInvokerRequestExecutor">??
  12. ????????<ref??
  13. ????????????bean="httpInvokerRequestExecutor"?/>??
  14. ????</property>??
  15. </bean>??
  16. <bean??
  17. ????id="httpInvokerRequestExecutor"?class="org.springframework.remoting.httpinvoker.CommonsHttpInvokerRequestExecutor">??
  18. ????<property??
  19. ????????name="httpClient">??
  20. ????????<bean??
  21. ????????????class="org.apache.commons.httpclient.HttpClient">??
  22. ????????????<property??
  23. ????????????????name="connectionTimeout"??
  24. ????????????????value="2000"?/>??
  25. ????????????<property??
  26. ????????????????name="timeout"??
  27. ????????????????value="5000"?/>??
  28. ????????</bean>??
  29. ????</property>??
  30. </bean>??

通过HttpClient,我们可以配置超时时间timeout和连接超时connectionTimeout两个属性,这样,服务器执行操作时,如果超时就可以强行释放连接,这样可怜的tomcat不会因为HttpInvoke连接不释放而被累死!Spring HttpInvoke的实现(远程接口调用),以及效率提升!解决Read timed out有关问题?
?
执行操作!?
Spring HttpInvoke的实现(远程接口调用),以及效率提升!解决Read timed out有关问题
这时,转为org.springframework.remoting.httpinvoker.CommonsHttpInvokerRequestExecutor实现了!?
不过同事认为,这个效率还是不够高!!!?
再改,改什么?还是xml!?
Xml代码?Spring HttpInvoke的实现(远程接口调用),以及效率提升!解决Read timed out有关问题?Spring HttpInvoke的实现(远程接口调用),以及效率提升!解决Read timed out有关问题
  1. <bean??
  2. ????????id="httpInvokerRequestExecutor"??
  3. ????????class="org.springframework.remoting.httpinvoker.CommonsHttpInvokerRequestExecutor">??
  4. ????????<property??
  5. ????????????name="httpClient">??
  6. ????????????<bean??
  7. ????????????????class="org.apache.commons.httpclient.HttpClient">??
  8. ????????????????<property??
  9. ????????????????????name="connectionTimeout"??
  10. ????????????????????value="2000"?/>??
  11. ????????????????<property??
  12. ????????????????????name="timeout"??
  13. ????????????????????value="5000"?/>??
  14. ????????????????<property??
  15. ????????????????????name="httpConnectionManager">??
  16. ????????????????????<ref??
  17. ????????????????????????bean="multiThreadedHttpConnectionManager"?/>??
  18. ????????????????</property>??
  19. ????????????</bean>??
  20. ????????</property>??
  21. ????</bean>??
  22. ????<bean??
  23. ????????id="multiThreadedHttpConnectionManager"??
  24. ????????class="org.apache.commons.httpclient.MultiThreadedHttpConnectionManager">??
  25. ????????<property??
  26. ????????????name="params">??
  27. ????????????<bean??
  28. ????????????????class="org.apache.commons.httpclient.params.HttpConnectionManagerParams">??
  29. ????????????????<property??
  30. ????????????????????name="maxTotalConnections"??
  31. ????????????????????value="600"?/>??
  32. ????????????????<property??
  33. ????????????????????name="defaultMaxConnectionsPerHost"??
  34. ????????????????????value="512"?/>??
  35. ????????????</bean>??
  36. ????????</property>??
  37. ????</bean>??

改用MultiThreadedHttpConnectionManager,多线程!!!?
测试就不说了,实践证明:?
默认实现,服务器平均10s左右才能响应一个请求。?
多线程实现,服务器平均20ms左右响应一个请求。?
这简直不是一个数量级!!!?

注意:以上是用HttpClient的3.1版本以下的Jar包,3.1以上已不支持如下配置,相应的方法已经废弃!?
Xml代码?Spring HttpInvoke的实现(远程接口调用),以及效率提升!解决Read timed out有关问题?Spring HttpInvoke的实现(远程接口调用),以及效率提升!解决Read timed out有关问题
  1. <property????
  2. ????name="connectionTimeout"????
  3. ????value="2000"?/>????
  4. <property????
  5. ????name="timeout"????
  6. ????value="5000"?/>???

如果仔细看看文档,?
引用HttpClient that uses a default MultiThreadedHttpConnectionManager.
commons 系列的实现怎么会不考虑多线程呢?人家默认实现就是多线程的!同时多虑了!?
当然,同时还补充了一句,需要控制连接数!?

好了 附上 新的HttpClient 优化 配置: (httpclient 3.1jar包以上版本)

Xml代码?

  1. <!--?调用?远程?openAPI?-->??
  2. ????<bean?id="userService"??
  3. ????????class="org.springframework.remoting.httpinvoker.HttpInvokerProxyFactoryBean">??
  4. ????????<property?name="serviceUrl"??
  5. ????????????value="http://localhost:8080/spring/service"?/>??
  6. ????????<property?name="serviceInterface"?value="org.zlex.spring.httpinvoke.service.UserService"?/>??
  7. ????????<!--?into?project?引用?配置方案?-->??
  8. ????????<property?name="httpInvokerRequestExecutor"?ref="httpInvokerRequestExecutor"?/>??
  9. ????</bean>??
  10. ?<!--?httpinvoker?optimization?让?httpinvoker转用?HttpClient通信,不使用默认的?SimpleHttpInvokerRequestExecutor?-->??
  11. ????<bean?id="httpInvokerRequestExecutor"?class="org.springframework.remoting.httpinvoker.CommonsHttpInvokerRequestExecutor">????
  12. ????????<property?name="httpClient"?ref="httpclient"?/>????
  13. ????</bean>??
  14. ??????
  15. ????<!--??HttpClient?启用Apache?HttpClient?通信?-->??
  16. ????<bean?id="httpclient"?class="org.apache.commons.httpclient.HttpClient">????
  17. ????????<constructor-arg>????
  18. ????????????<ref?bean="connectionManager"/>????
  19. ????????</constructor-arg>?????
  20. ????</bean>??
  21. ??????
  22. ????<!--?http管理参数配置?-->??
  23. ????<bean?id="connectionManager"?class="org.apache.commons.httpclient.MultiThreadedHttpConnectionManager">????
  24. ????????<property?name="params"?ref="connectionManagerParams"/>????
  25. ????</bean>???
  26. ??????
  27. ????<!--?httpclient线程池?-->????
  28. ????<bean?id="connectionManagerParams"?class="org.apache.commons.httpclient.params.HttpConnectionManagerParams">??
  29. ????????<!--?设置?连接超时时间(毫秒),默认为0不使用?-->????
  30. ????????<property?name="connectionTimeout"?value="5000"/>???
  31. ????????<!--?设置?读取数据超时(毫秒),默认为0不使用?-->???
  32. ????????<property?name="soTimeout"?value="10000"/>????
  33. ????????<!--?设置默认的连接数量最大允许对一个给定的主机配置?-->??
  34. ????????<property?name="maxTotalConnections"?value="30"/>??
  35. ????????<!--?允许的最大连接数?-->????
  36. ????????<property?name="defaultMaxConnectionsPerHost"?value="20"/>????
  37. ????</bean>? ?

?


里面的connectionManagerParams配置 根据你的 环境 改变它吧...变成最优方案^_^...

另外,用spring 默认的?SimpleHttpInvokerRequestExecutor 时, 有时会产生

?java.net.SocketTimeoutException: Read timed out

?Could not access HTTP invoker remote service at [http://localhost:8080/spring/service]; nested exception is java.net.SocketTimeoutException: Read timed out?

就是没有设置 超时 或 连接溢出 超时的没释放. 所以除了演示外其他项目都要改成 上面 新的配置,?让?httpinvoker转用 apache?HttpClient通信,不使用默认的?SimpleHttpInvokerRequestExecutor。虽然底层一样,但处理上不一样。

OK了 完结!!! (有时间 再发表SimpleHttpInvokerRequestExecutor的源码)...

1 楼 yuhui0531 2012-04-19 没源码没几把

读书人网 >软件开发

热点推荐