Hessian怎样实现远程调用

1.Spring中除了提供HTTP调用器方式的远程调用,还对第三方的远程调用实现提供了支持,其中提供了对Hessian的支持。

Hessian是由Caocho公司发布的一个轻量级的二进制协议远程调用实现方案,Hessian也是基于HTTP协议的,其工作原理如下:

(1).客户端:

a.发送远程调用请求:

客户端程序—>发送远程调用请求—>Hessian客户端拦截器—>封装远程调用请求—>Hessian代理—>通过HTTP协议发送远程请求代理到服务端。

b.接收远程调用响应:

远程调用结果—>HTTP响应—>客户端。

(1).服务端:

a.接收远程调用请求:

远程调用HTTP请求—>HessianServiceExporter接收请求—>HessianExporter将远程调用对象封装为HessianSkeleton框架—> HessianSkeleton处理远程调用请求。

b.返回远程调用响应:

HessianSkeleton封装远程调用处理结果—>HTTP响应—>客户端。

本文章通过分析Spring对Hessian支持的相关源码,了解Spring对Hessian支持的具体实现。

2.Hessian的客户端配置:

Hessian的客户端需要做类似如下的配置:

[xhtml] view plaincopy

  1. <bean id=”hessianProxy” class=”org.springframework.remoting.caucho.HessianProxyFactoryBean”>

  2. <property name=”serviceUrl”>

  3. <value>http://hostAddress:8080/serviceUrlvalue>

  4. property>

  5. <property name=”serviceInterface”>

  6. <value>远程调用服务接口value>

  7. ]property>

  8. bean>

和HTTP调用器的配置类似,都需要配置远程调用请求的url,这个url要和服务端的url一致,Spring通过DispatcherServlet找到服务端对于的请求url。

HessianProxyFactoryBean是Spring中管理Hessian客户端的IoC容器,主要负责产生服务端远程调用代理和对客户端远程调用的拦截器设置。

3.HessianProxyFactoryBean:

HessianProxyFactoryBean生成远程调用代理和客户端远程调用拦截器设置,其源码如下:

[java] view plaincopy

public class HessianProxyFactoryBean extends HessianClientInterceptor implements FactoryBean {

  • //远程调用代理对象

  • private Object serviceProxy;

  • //Spring IoC容器依赖注入完成后的回调方法

  • public void afterPropertiesSet() {

  • //首先调用父类HessianClientInterceptor的回调方法

  • super.afterPropertiesSet();

  • //创建远程调用代理对象并设置拦截器,注意这个this参数,因为//HessianProxyFactoryBean继承HessianClientInterceptor,因此其本身也

  • //是Hassien客户端拦截器

  • this.serviceProxy = new ProxyFactory(getServiceInterface(), this).getProxy(getBeanClassLoader());

  • }

  • //Spring IoC容器的接口FactoryBean产生对象的方法,客户端通过该方法获取被管

  • //理的远程调用代理

  • public Object getObject() {

  • return this.serviceProxy;

  • }

  • //获取对象的类型

  • public Class getObjectType() {

  • return getServiceInterface();

  • }

  • //对象是否是单态类型,Spring默认管理的对象都是单态模式

  • public boolean isSingleton() {

  • return true;

  • }

  • }

HessianProxyFactoryBean最核心的功能就是在IoC容器回调方法中产生远程调用代理对象,在产生远程调用代理对象时,将代理对象的拦截器设置为其父类HessianClientInterceptor。

4.HessianClientInterceptor拦截客户端的远程调用请求:

HessianClientInterceptor对客户端的远程调用进行拦截,为客户端的远程调用创建Hessian代理,通过Hessian代理调用服务端远程调用对象,其源码如下:

[java] view plaincopy

  1. public class HessianClientInterceptor extends UrlBasedRemoteAccessor implements MethodInterceptor {

  2. //创建Hessiann代理工厂

  3. private HessianProxyFactory proxyFactory = new HessianProxyFactory();

  4. //Hessian代理

  5. private Object hessianProxy;

  6. //设置Hessian代理工厂

  7. public void setProxyFactory(HessianProxyFactory proxyFactory) {

  8. this.proxyFactory = (proxyFactory != null ? proxyFactory : new HessianProxyFactory());

  9. }

  10. //设置Hessian序列化工厂

  11. public void setSerializerFactory(SerializerFactory serializerFactory) {

  12. this.proxyFactory.setSerializerFactory(serializerFactory);

  13. }

  14. //设置Hessian是否发送java集合类型对象

  15. public void setSendCollectionType(boolean sendCollectionType) {

  16. this.proxyFactory.getSerializerFactory().setSendCollectionType(sendCollectionType);

  17. }

  18. //设置远程调用时是否重载方法

  19. public void setOverloadEnabled(boolean overloadEnabled) {

  20. this.proxyFactory.setOverloadEnabled(overloadEnabled);

  21. }

  22. //设置远程调用用户名

  23. public void setUsername(String username) {

  24. this.proxyFactory.setUser(username);

  25. }

  26. //设置远程调用密码

  27. public void setPassword(String password) {

  28. this.proxyFactory.setPassword(password);

  29. }

  30. //设置是否使用Hessian的Debug调试模式

  31. public void setDebug(boolean debug) {

  32. this.proxyFactory.setDebug(debug);

  33. }

  34. //设置是否使用chunked端口发送Hessian请求

  35. public void setChunkedPost(boolean chunkedPost) {

  36. this.proxyFactory.setChunkedPost(chunkedPost);

  37. }

  38. //设置Hessian等待响应的超时时长

  39. public void setReadTimeout(long timeout) {

  40. this.proxyFactory.setReadTimeout(timeout);

  41. }

  42. //设置是否使用Hessain版本2协议解析请求和响应

  43. public void setHessian2(boolean hessian2) {

  44. this.proxyFactory.setHessian2Request(hessian2);

  45. this.proxyFactory.setHessian2Reply(hessian2);

  46. }

  47. //设置是否使用Hessian版本2协议解析请求

  48. public void setHessian2Request(boolean hessian2) {

  49. this.proxyFactory.setHessian2Request(hessian2);

  50. }

  51. //设置是否使用Hessian版本2协议解析响应

  52. public void setHessian2Reply(boolean hessian2) {

  53. this.proxyFactory.setHessian2Reply(hessian2);

  54. }

  55. //子类HessianProxyFactoryBean的回调方法调用此回调方法

  56. public void afterPropertiesSet() {

  57. //调用其父类UrlBasedRemoteAccessor的回调方法获取客户端配置的请求url

  58. super.afterPropertiesSet();

  59. //初始化Hessian代理

  60. prepare();

  61. }

  62. //初始化Hessian代理

  63. public void prepare() throws RemoteLookupFailureException {

  64. try {

  65. //创建Hessian代理

  66. this.hessianProxy = createHessianProxy(this.proxyFactory);

  67. }

  68. catch (MalformedURLException ex) {

  69. throw new RemoteLookupFailureException("Service URL [" + getServiceUrl() + "] is invalid", ex);

  70. }

  71. }

  72. //创建Hessian代理

  73. protected Object createHessianProxy(HessianProxyFactory proxyFactory) throws MalformedURLException {

  74. Assert.notNull(getServiceInterface(), "'serviceInterface' is required");

  75. //使用Hessian代理工厂创建Hessian代理

  76. return proxyFactory.create(getServiceInterface(), getServiceUrl());

  77. }

  78. //拦截器客户端请求的方法

  79. public Object invoke(MethodInvocation invocation) throws Throwable {

  80. if (this.hessianProxy == null) {

  81. throw new IllegalStateException("HessianClientInterceptor is not properly initialized - " +

  82. "invoke 'prepare' before attempting any operations");

  83. }

  84. //获取当前环境中线程类加载器

  85. ClassLoader originalClassLoader = overrideThreadContextClassLoader();

  86. try {

  87. //调用Hessian代理的方法,是Hessian远程调用的入口方法,使用JDK反射机制

  88. return invocation.getMethod().invoke(this.hessianProxy, invocation.getArguments());

  89. }

  90. //处理Hessian远程调用中的异常

  91. catch (InvocationTargetException ex) {

  92. Throwable targetEx = ex.getTargetException();

  93. if (targetEx instanceof InvocationTargetException) {

  94. targetEx = ((InvocationTargetException) targetEx).getTargetException();

  95. }

  96. if (targetEx instanceof HessianConnectionException) {

  97. throw convertHessianAccessException(targetEx);

  98. }

  99. else if (targetEx instanceof HessianException || targetEx instanceof HessianRuntimeException) {

  100. Throwable cause = targetEx.getCause();

  101. throw convertHessianAccessException(cause != null ? cause : targetEx);

  102. }

  103. else if (targetEx instanceof UndeclaredThrowableException) {

  104. UndeclaredThrowableException utex = (UndeclaredThrowableException) targetEx;

  105. throw convertHessianAccessException(utex.getUndeclaredThrowable());

  106. }

  107. else {

  108. throw targetEx;

  109. }

  110. }

  111. catch (Throwable ex) {

  112. throw new RemoteProxyFailureException(

  113. "Failed to invoke Hessian proxy for remote service [" + getServiceUrl() + "]", ex);

  114. }

  115. //重置类加载器

  116. finally {

  117. resetThreadContextClassLoader(originalClassLoader);

  118. }

  119. }

  120. //将Hessian异常转换为Spring远程调用异常

  121. protected RemoteAccessException convertHessianAccessException(Throwable ex) {

  122. if (ex instanceof HessianConnectionException || ex instanceof ConnectException) {

  123. return new RemoteConnectFailureException(

  124. "Cannot connect to Hessian remote service at [" + getServiceUrl() + "]", ex);

  125. }

  126. else {

  127. return new RemoteAccessException(

  128. "Cannot access Hessian remote service at [" + getServiceUrl() + "]", ex);

  129. }

  130. }

  131. }

通过上面对HessianClientInterceptor的源码分析,我们可以看到Hessian客户端拦截器提供的最重要的方法是对远程调用拦截的方法invoke,在该方法中使用JDK的反射机制调用Hessian代理对象的指定方法。而Hessian代理是由Hessain代理器工厂HessianProxyFactory产生的,这个Hessian代理器工厂是有Hessian提供的。

5.Hessian服务器端配置:

在Hessian的服务端需要进行类似如下的配置:

[xhtml] view plaincopy

  1. <bean id=”/serviceUrl” class=”org.springframework.remoting.caucho.HessianServiceExporter”>

  2. <property name=”service”>

  3. <ref bean=”service”/>

  4. property>

  5. <property name=”serviceInterface”>

  6. <value>远程服务接口value>

  7. property>

  8. bean>

Spring的HessianServiceExporter把远程调用服务整合到Spring MVC框架中,通过DispatcherServlet将客户端请求转发到服务器端相应的请求url远程对象上。

注意:serviceUrl要和客户端serviceUrl中配置的相同,service即是服务端提供服务的远程对象。

6.HessianServiceExporter处理Hessian远程调用请求:

HessianServiceExporter接收客户端的远程调用请求,并调用HessianExporter具体处理远程调用,并且远程调用结果封装到HTTP响应中返回,e家装网源码如下:

[java] view plaincopy

  1. public class HessianServiceExporter extends HessianExporter implements HttpRequestHandler {

  2. //处理Hessian请求,并将处理结果封装为Hessian响应返回

  3. public void handleRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

  4. //Hessian只支持HTTP的POST方法

  5. if (!"POST".equals(request.getMethod())) {

  6. throw new HttpRequestMethodNotSupportedException(request.getMethod(),

  7. new String[] {"POST"}, "HessianServiceExporter only supports POST requests");

  8. }

  9. //设置Hessian响应内容类型为:application/x-hessian

  10. response.setContentType(CONTENT_TYPE_HESSIAN);

  11. try {

  12. //HessianExporter真正处理Hessian请求和封装Hessian响应的方法

  13. invoke(request.getInputStream(), response.getOutputStream());

  14. }

  15. catch (Throwable ex) {

  16. throw new NestedServletException("Hessian skeleton invocation failed", ex);

  17. }

  18. }

  19. }

7.HessianExporter处理Hessian请求并将结果封装为HTTP响应:

[java] view plaincopy

  1. public class HessianExporter extends RemoteExporter implements InitializingBean {

  2. //Hessian HTTP响应内容类型

  3. public static final String CONTENT_TYPE_HESSIAN = "application/x-hessian";

  4. //Hessian序列化工厂

  5. private SerializerFactory serializerFactory = new SerializerFactory();

  6. //Hessian Debug日志

  7. private Log debugLogger;

  8. //Hessian服务端框架

  9. private HessianSkeleton skeleton;

  10. //设置Hessian序列化工厂

  11. public void setSerializerFactory(SerializerFactory serializerFactory) {

  12. this.serializerFactory = (serializerFactory != null ? serializerFactory : new SerializerFactory());

  13. }

  14. //设置序列化集合发送java集合类型

  15. public void setSendCollectionType(boolean sendCollectionType) {

  16. this.serializerFactory.setSendCollectionType(sendCollectionType);

  17. }

  18. //设置Hessian调试模式

  19. public void setDebug(boolean debug) {

  20. this.debugLogger = (debug ? logger : null);

  21. }

  22. //回调方法

  23. public void afterPropertiesSet() {

  24. prepare();

  25. }

  26. //初始化Hessian服务框架

  27. public void prepare() {

  28. //这里调用父类RemoteExporter的方法检查服务提供类、服务接口

  29. checkService();

  30. checkServiceInterface();

  31. //创建远程服务类的Hessian框架

  32. this.skeleton = new HessianSkeleton(getProxyForService(), getServiceInterface());

  33. }

  34. //远程调用处理入口

  35. public void invoke(InputStream inputStream, OutputStream outputStream) throws Throwable {

  36. Assert.notNull(this.skeleton, "Hessian exporter has not been initialized");

  37. doInvoke(this.skeleton, inputStream, outputStream);

  38. }

  39. //远程调用处理方法

  40. protected void doInvoke(HessianSkeleton skeleton, InputStream inputStream, OutputStream outputStream)

  41. throws Throwable {

  42. //获取类加载器

  43. ClassLoader originalClassLoader = overrideThreadContextClassLoader();

  44. try {

  45. InputStream isToUse = inputStream;

  46. OutputStream osToUse = outputStream;

  47. //设置Hessian调试日志

  48. if (this.debugLogger != null && this.debugLogger.isDebugEnabled()) {

  49. PrintWriter debugWriter = new PrintWriter(new CommonsLogWriter(this.debugLogger));

  50. HessianDebugInputStream dis = new HessianDebugInputStream(inputStream, debugWriter);

  51. dis.startTop2();

  52. HessianDebugOutputStream dos = new HessianDebugOutputStream(outputStream, debugWriter);

  53. dos.startTop2();

  54. isToUse = dis;

  55. osToUse = dos;

  56. }

  57. if (!isToUse.markSupported()) {

  58. isToUse = new BufferedInputStream(isToUse);

  59. isToUse.mark(1);

  60. }

  61. int code = isToUse.read();

  62. int major;

  63. int minor;

  64. AbstractHessianInput in;

  65. AbstractHessianOutput out;

  66. //根据客户端不同的Hessian版本,设置不同的Hessian抽象输入/输出

  67. //Hessian2.0

  68. if (code == 'H') {

  69. major = isToUse.read();

  70. minor = isToUse.read();

  71. if (major != 0x02) {

  72. throw new IOException("Version " + major + "." + minor + " is not understood");

  73. }

  74. in = new Hessian2Input(isToUse);

  75. out = new Hessian2Output(osToUse);

  76. in.readCall();

  77. }

  78. //Hessian2.0

  79. else if (code == 'C') {

  80. isToUse.reset();

  81. in = new Hessian2Input(isToUse);

  82. out = new Hessian2Output(osToUse);

  83. in.readCall();

  84. }

  85. //Hessian1.0

  86. else if (code == 'c') {

  87. major = isToUse.read();

  88. minor = isToUse.read();

  89. in = new HessianInput(isToUse);

  90. if (major >= 2) {

  91. out = new Hessian2Output(osToUse);

  92. }

  93. else {

  94. out = new HessianOutput(osToUse);

  95. }

  96. }

  97. else {

  98. throw new IOException("Expected 'H'/'C' (Hessian 2.0) or 'c' (Hessian 1.0) in hessian input at " + code);

  99. }

  100. //设置Hessian序列化工厂

  101. if (this.serializerFactory != null) {

  102. in.setSerializerFactory(this.serializerFactory);

  103. out.setSerializerFactory(this.serializerFactory);

  104. }

  105. try {

  106. //通过服务端远程对象的Hessian框架处理远程调用

  107. skeleton.invoke(in, out);

  108. }

  109. finally {

  110. try {

  111. in.close();

  112. isToUse.close();

  113. }

  114. catch (IOException ex) {

  115. }

  116. try {

  117. out.close();

  118. osToUse.close();

  119. }

  120. catch (IOException ex) {

  121. }

  122. }

  123. }

  124. //重置类加载器

  125. finally {

  126. resetThreadContextClassLoader(originalClassLoader);

  127. }

  128. }

  129. }

通过上面对HessianExporter源码分析,我们看到真正进行远程调用处理的是由Hessian提供的服务端HessianSkeleton。

上一篇:基于Dubbo的Hessian协议实现远程调用


下一篇:ZeroC ICE的远程调用框架 AMD