hessian远程调用的使用

一.      远程通讯协议的基本原理

网络通信需要做的就是将流从一台计算机传输到另外一台计算机,基于传输协议和网络 IO 来实现,其中传输协议比较出名的有 http 、 tcp 、 udp 等等, http 、 tcp 、 udp 都是在基于 Socket 概念上为某类应用场景而扩展出的传输协议,网络 IO ,主要有 bio 、 nio 、 aio 三种方式,所有的分布式应用通讯都基于这个原理而实现,只是为了应用的易用,各种语言通常都会提供一些更为贴近应用易用的应用层协议。

二.      应用级协议 Binary-RPC

Binary-RPC 是一种和 RMI 类似的远程调用的协议,它和 RMI 的不同之处在于它以标准的二进制格式来定义请求的信息 ( 请求的对象、方法、参数等 ) ,这样的好处是什么呢,就是在跨语言通讯的时候也可以使用。

来看下 Binary -RPC 协议的一次远程通信过程:

 

1 、客户端发起请求,按照 Binary -RPC 协议将请求信息进行填充;

2 、填充完毕后将二进制格式文件转化为流,通过传输协议进行传输;

3 、接收到在接收到流后转换为二进制格式文件,按照 Binary -RPC 协议获取请求的信息并进行处理;

4 、处理完毕后将结果按照 Binary -RPC 协议写入二进制格式文件中并返回。

         问题总结:

1 、传输的标准格式是?

    标准格式的二进制文件。

2 、怎么样将请求转化为传输的流?

    将二进制格式文件转化为流。

3 、怎么接收和处理流?

    通过监听的端口获取到请求的流,转化为二进制文件,根据协议获取请求的信息,进行处理并将结果写入 XML 中返回。

4 、传输协议是?

Http 。

三.      Hessian ——一种实现远程通讯的 library

Hessian 是由 caucho 提供的一个基于 binary-RPC 实现的远程通讯 library 。

1 、是基于什么协议实现的?

基于 Binary-RPC 协议实现。

2 、怎么发起请求?

需通过 Hessian 本身提供的 API 来发起请求。

3 、怎么将请求转化为符合协议的格式的?

Hessian 通过其自定义的串行化机制将请求信息进行序列化,产生二进制流。

4 、使用什么传输协议传输?

Hessian 基于 Http 协议进行传输。

5 、响应端基于什么机制来接收请求?

响应端根据 Hessian 提供的 API 来接收请求。

6 、怎么将流还原为传输格式的?

Hessian 根据其私有的串行化机制来将请求信息进行反序列化,传递给使用者时已是相应的请求信息对象了。

7 、处理完毕后怎么回应?

 处理完毕后直接返回, hessian 将结果对象进行序列化,传输至调用端。


hessian是一个轻量级的基于 Binary-RPC 协议实现通过http协议进行传输的远程,还要依赖于servlet进行配置。使用简单的方法提供了RMI的功能。 相比WebService,Hessian更简单、快捷。采用的是二进制RPC协议,因为采用的是二进制协议,所以它很适合于发送二进制数据。

dubbo支持多种远程调用方式,例如dubbo RPC(二进制序列化 + tcp协议)、http invoker(二进制序列化 +http协议,至少在开源版本没发现对文本序列化的支持)、hessian(二进制序列化 + http协议)、WebServices (文本序列化 + http协议

hessian开发流程

1,服务端要注意的事项:

    (1)导入hessian的jar包

    (2)设计一个接口来供客户端调用

    (3)实现该接口的实现类

    (4)web.xml配置,配置相应的servlet

    (5)由于采用二进制RPC协议传输数据,对象必须序列化,实现Serializable 接口。

     ( 6 ) 对于复杂对象可以使用Map的方法传递

2、客户端必须具备以下几点:

    (1)java客户端包含Hessian.jar的包。C#中引用hessianCSharp.dll

    (2)具有和服务器端结构一样的接口。包括命名空间都最好一样

    (3)利用HessianProxyFactory调用远程接口。

实例如下:

服务端使用web project项目,首先是数据类:User

package com.jary.hessian;
import java.io.*;

public class User implements Serializable{
   private String name;
   private Integer age;
   public User(){}
public User(String name, Integer age) {
	super();
	this.name = name;
	this.age = age;
}
public String getName() {
	return name;
}
public void setName(String name) {
	this.name = name;
}
public Integer getAge() {
	return age;
}
public void setAge(Integer age) {
	this.age = age;
}
   
}
2、服务端接口IBasicApi

package com.jary.hessian;
public interface IBasicApi {
    //设置用户名
	public boolean setUserName(String name);
	//获取问候
	public String sayHello();
	//获取用户信息
	public User getUser();
}
3、接口的实现

package com.jary.hessian;

public class IBasicService implements IBasicApi {
    private String name;
	@Override
	public boolean setUserName(String name) {
		this.name=name;
		return false;
	}

	@Override
	public String sayHello() {
		
		return "Hello"+name+"welcome to hessian";
	}

	@Override
	public User getUser() {
		User user=new User(name,23);
		return user;
	}

}
4、服务端applicationContext-hessian.xml配置name="/<span style="color: rgb(255, 0, 0); font-family: Arial, Helvetica, sans-serif;">HessianRemoteServlet" 要有斜杠才行</span>
"

<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
<bean id="ibasicService" class="com.jary.hessian.IBasicService" />
<bean <span style="color:#ff0000;">name="/HessianRemoteServlet"</span>	class="org.springframework.remoting.caucho.HessianServiceExporter">
		<property name="service" ref="ibasicService" />
		<property name="serviceInterface" value="com.jary.hessian.IBasicApi" />	
</bean>
</beans>
5、web.xml配置servlet(基于spring整合后)

<!-- hessian和spring的整合配置 -->
	<servlet>
		<servlet-name>HessianRemoteServlet</servlet-name>
		<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
		<init-param>
			<param-name>contextConfigLocation</param-name>
			<param-value>classpath:applicationContext-hessian.xml</param-value>
		</init-param>

	</servlet>
	<servlet-mapping>
		<servlet-name>HessianRemoteServlet</servlet-name>
		<url-pattern>/remote/api/*</url-pattern>
	</servlet-mapping>
6、客户端使用java project项目,客户端也要导入hessian和spring的包后,配置applicationContext-hessianclient.xml

<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
<bean id="HessianRemoteServlet" class="org.springframework.remoting.caucho.HessianProxyFactoryBean">
<property name="serviceUrl" value="http://localhost:8080/HessianDemo/remote/api/HessianRemoteServlet"/>
<property name="serviceInterface" value="com.jary.hessiantest.IBasicApi"/>
</bean>
</beans>
7、客户端的调用测试类

package com.jary.hessiantest;
import java.net.MalformedURLException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.caucho.hessian.client.HessianProxyFactory;

/*
 * 创建客户端测试hessian远程调用
 */
public class HessianClientTest {
   
   public static void main(String[] args) throws MalformedURLException {
	String url="http://localhost:8080/HessianDemo/remote/api/HessianRemoteServlet";
	HessianProxyFactory factory=new HessianProxyFactory();
	IBasicApi iBasicApi = (IBasicApi) factory.create(IBasicApi.class, url);
	
	ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext-hessianclient.xml");
	IBasicApi api = (IBasicApi) context.getBean("HessianRemoteServlet");
	iBasicApi.setUserName("张三");
	System.out.println(iBasicApi.sayHello());
	System.out.println(iBasicApi.getUser().getName());
	System.out.println(iBasicApi.getUser().getAge());
}
}
输出结果:hello 张三 welcome to hessian

           张三

           23

四.      Hessian 源码分析

以 hessian 和 spring dm server 整合环境为例。

  1. 1.     客户端发起请求

Hessian 的这个远程过程调用,完全使用动态代理来实现的。有客户端可以看出。

除去 spring 对其的封装,客户端主要是通过 HessianProxyFactory 的 create 方法就是创建接口的代理类,该类实现了接口, JDK 的 proxy 类会自动用 InvocationHandler 的实现类(该类在 Hessian 中表现为 HessianProxy )的 invoke 方法体来填充所生成代理类的方法体。

客户端系统启动时:

         根据 serviceUrl 和 serviceInterface 创建代理。

         HessianProxyFactoryBean 类

        

HessianClientInterceptor 类

                  createHessianProxy(HessianProxyFactory proxyFactory)

 

HessianProxyFactory 类

                  public Object create(Class api, String urlName)

 

客户端调用 hessian 服务时:

                   HessianProxy 类的 invoke(Object proxy, Method method, Object []args) 方法

                            String methodName = method.getName();// 取得方法名

                            Object value = args[0]; // 取得传入参数

                            conn = sendRequest(mangleName, args) ;      // 通过该方法和服务器端取得连接

 

                            httpConn = (HttpURLConnection) conn;

                            code = httpConn.getResponseCode();    // 发出请求

 

// 等待服务器端返回相应…………

 

                            is = conn.getInputStream();

                            Object value = in.readObject(method.getReturnType()); // 取得返回值

 

HessianProxy 类的 URLConnection sendRequest(String methodName, Object []args) 方法:

                      URLConnection  conn = _factory.openConnection(_url);      // 创建 URLConnection 

                            OutputStream os = conn.getOutputStream();

 

                            AbstractHessianOutput out = _factory.getHessianOutput(os); // 封装为 hessian 自己的输入输出 API

                            out.call(methodName, args);

                            return conn;

        

 

  1. 2.     服务器端接收请求并处理请求

服务器端截获相应请求交给:

org.springframework.remoting.caucho.HessianServiceExporter

具体处理步骤如下:

a)       HessianServiceExporter 类

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

 

b)       HessianExporter 类

(Hessian2SkeletonInvoker) this.skeletonInvoker.invoke(inputStream, outputStream);

c)       Hessian2SkeletonInvoker 类

将输入输出封转化为转化为 Hessian 特有的 Hessian2Input 和 Hessian2Output

      Hessian2Input in = new Hessian2Input(isToUse);

      in.setSerializerFactory(this.serializerFactory);

 

      AbstractHessianOutput out = null;

      int major = in.read();

      int minor = in.read();

      out = new Hessian2Output(osToUse);

      out = new HessianOutput(osToUse);

      out.setSerializerFactory(this.serializerFactory);

      (HessianSkeleton) this.skeleton.invoke(in, out);

 

d)       HessianSkeleton 类

           读取方法名

         String methodName = in.readMethod();

    Method method = getMethod(methodName);

 

           读取方法参数

         Class []args = method.getParameterTypes();

    Object []values = new Object[args.length];

 

           执行相应方法并取得结果

         result = method.invoke(service, values);

 

           结果写入到输出流

         out.writeObject(result);

        

总结: 由上面源码分析可知,客户端发起请求和服务器端接收处理请求都是通过 hessian 自己的 API 。输入输出流都要封装为 hessian 自己的 Hessian2Input 和 Hessian2Output ,接下来一节我们将去了解 hessian 自己封装的输入输出到底做了些什么!

五.      Hessian 的序列化和反序列化实现

hessian 源码中 com.caucho.hessian.io 这个包是 hessian 实现序列化与反序列化的核心包。其中 AbstractSerializerFactory , AbstractHessianOutput , AbstractSerializer , AbstractHessianInput , AbstractDeserializer 是 hessian 实现序列化和反序列化的核心结构代码。

 

  1. AbstractSerializerFactory ,它有 2 个抽象方法:

根据类来决定用哪种序列化工具类

abstract public Serializer getSerializer(Class cl)  throws HessianProtocolException; 

根据类来决定用哪种反序列化工具类

abstract public Deserializer getDeserializer(Class cl)  throws HessianProtocolException;

  1. SerializerFactory 继承 AbstractSerializerFactory 。

在 SerializerFactory 有很多静态 map 用来存放类与序列化和反序列化工具类的映射,这样如果已经用过的序列化工具就可以直接拿出来用,不必再重新实例化工具类。

在 SerializerFactory 中,实现了抽象类的 getSerializer 方法,根据不同的需要被序列化的类来获得不同的序列化工具,一共有 17 种序列化工具, hessian 为不同的类型的 java 对象实现了不同的序列化工具,默认的序列化工具是 JavaSerializer 。

在 SerializerFactory 中,也实现了抽象类的 getDeserializer 方法,根据不同的需要被反序列化的类来获得不同的反序列化工具,默认的反序列化工具类是 JavaDeserializer 。

  1. HessianOutput 继承 AbstractHessianOutput 成为序列化输出流的一种实现。

它会实现很多方法,用来做流输出。

需要注意的是方法,它会先调用 serializerFactory 根据类来获得 serializer 序列化工具类

public void writeObject(Object object)

throws IOException 

if (object == null) { 

writeNull(); 

return; 

 

Serializer serializer; 

 

serializer = _serializerFactory.getSerializer(object.getClass());  

 

serializer.writeObject(object, this); 

  1. 现在我们来看看 AbstractSerializer 。

其 writeObject 是必须在子类实现的方法, AbstractSerializer 有 17 种子类实现, hessian 根据不同的 java 对象类型来实现了不同的序列化工具类,其中默认的是 JavaSerializer 。

而 JavaSerializer 的 writeObject 方法的实现,遍历 java 对象的数据成员,根据数据成员的类型来获得各自的 FieldSerializer ,一共有 6 中默认的 FieldSerializer 。

拿默认的 FieldSerializer 举例,还是调用 AbstractHessianOutput 的子类来 writeObject ,这个时候,肯定能找到相应的 Serializer 来做序列化

 

同理可以反推出 hessian 的反序列化机制。 SerializerFactory 可以根据需要被反序列化的类来获得反序列化工具类来做反序列化操作。

 

总结:得益于 hessian 序列号和反序列化的实现机制, hessian 序列化的速度很快,而且序列化后的字节数也较其他技术少。

 

 

 

 

 

参考文献:

  1. 《 Java 远程通讯可选技术及原理》 http://java.chinaitlab.com/base/740383.html
  2. 《 Hessian-3.2.0 源码》
  3. 《 hessian 序列化实现初探》 http://www.javaeye.com/topic/245238
  4. 《 Hessian 2.0 序列化协议规范》

http://blog.csdn.net/xpspace/archive/2007/10/05/1811603.aspx



  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值