Hessian是比較经常使用的binary-rpc。性能较高,适合互联网应用。主要使用在普通的webservice 方法调用。交互数据较小的场景中。hessian的数据交互基于http协议,通常hessian的server端设计须要使用到web server容器(比方servlet等)。你能够将不论什么Java类暴露给HessianServlet,并公布成hessian服务;那么hessian client将能够通过类似调用servlet一样,获得远程方法的输出结果。

由于hessian的接口调用基于http,且以字节码的方式进行数据交换,那么hessian须要提供自己定义的“protocol”以及序列化/反序列机制。

通常我们觉得hessian的这样的方式是高效并且简洁的,只是hessian中使用的反射机制/序列化机制/动态代理都是基于java原生API。(Java自带的序列化是否高效,在此就不在争论了)

一.Hessian原理与协议简析:

http的协议约定了传输数据的方式,hessian也无法改变太多:

1) hessian中client与server的交互,基于http-post方式。

2) hessian将辅助信息。封装在http header中,比方“授权token”等,我们能够基于http-header来封装关于“安全校验”“meta数据”等。hessian提供了简单的"校验"机制。

3) 对于hessian的交互核心数据,比方“调用的方法”和參数列表信息。将通过post请求的body体直接发送。格式为字节流。

4) 对于hessian的server端响应数据,将在response中通过字节流的方式直接输出。

hessian的协议本身并不复杂。在此不再赘言。所谓协议(protocol)就是约束数据的格式,client依照协议将请求信息序列化成字节序列发送给server端,server端依据协议,将数据反序列化成“对象”,然后运行指定的方法。并将方法的返回值再次依照协议序列化成字节流,响应给client,client依照协议将字节流反序列话成"对象"。

Client端:

在Client端,核心API为:

1) HessianProxyFactory: 负责托管"远程接口"和"远程hessian服务的URL",并生成代理类(Java Proxy实例)。

2) HessianProxy: Proxy实例的驱动器(handler),当代理实例的方法调用时,HessianProxy负责序列化"方法名"/"參数列表"等,并调用远程URL获取响应数据;同一时候也负责反序列化。

底层使用HttpURLConnection。

3) HessianOutput: 负责将序列化的字节数据,依照协议,写入inputStream,并通过URL Connection发送给远端。

hessian-client发送请求的首要条件,就是须要指明url,此url就是server端暴露的servlet地址。

  1. HessianProxyFactory proxyFactory = new HessianProxyFactory();
  2. HelloService service = (HelloService)proxyFactory.create(HelloService.class, "http://localhost:8080/hessian/helloService");
  3. System.out.println(service.sayHello("hessian"));

上述代码例子中,HelloService表示远程接口API,当中URL中“helloService”表示调用的“服务名称”,这个“服务名称”有server端决定。所以Client须要首先知道所需服务的URL全路径。

那么方法的调用数据,将会依照例如以下“序列”发送(俗称“字节码成帧”):

  1. [“方法名称“的字节长度]["方法名称”字节序列][參数的个数]{[參数类型][”參数“的字节长度][”參数“的字节序列]...}

比方调用sayHello(String message)方法,那么序列化的字节流格式可能为:

  1. ][sayHello][1]['S'][5]['hello']
  2. //当中“8”表示“sayHello”方法名称为8个字节
  3. //"1"表示參数的个数为1
  4. //“S”表示參数的类型为“String”,hessian中定义了大量的简写字母。用来表示java数据类型
  5. //“5”表示參数的字节个数为5
  6. //"hello"表示此參数的值为“hello”,只是实际上传输的应该是“hello”相应的字节序列。

假设你从事过socket通信方面的开发。你应该知道server端会怎样“解析”这个字节流信息。对于Hessian-server端而言,也是依据“字节码成帧”,逐个读取信息,比方首先读取一个32位的int。得到“8”,然后读取8个字节并使用utf8的方式编码成String字符串,将获得“sayHello”,此时server端已经能够知道client须要调用的方法名称为“sayHello”。然后对于一个32为的int。得到1,表示參数列表的个数为1;然后在读取一个字节。获得“S”,表示參数为String类型.....

Server端运行结束后,Http响应的字节流格式基本和上述类似。

那么HessianProxy负责反序列化和类型转换就可以。

须要注意。Hessian Client默认不支持“重载”方法的调用,通常我们须要开启“OverloadEnabled”属性设置为true。此后在方法调用时。Hessian将会把“方法签名” + “_” + “參数类型”作为新的方法名称发送给Server端。

  1. proxyFactory.setOverloadEnabled(true);//默觉得false

Server端:

在Server端最重要的类,就是HessianServlet;它是一个普通的Java Servlet实现。每一个“服务”都须要配置一个HessianServlet实例,它负责接收Client发送的Http请求,请求类型必须是POST。

  1. <servlet>
  2. <servlet-name>helloService</servlet-name>
  3. <servlet-class>com.caucho.hessian.server.HessianServlet</servlet-class>
  4. <init-param>
  5. <param-name>service-class</param-name>
  6. <param-value>com.test.hessian.impl.HelloServiceImpl</param-value>
  7. </init-param>
  8. </servlet>
  9. <servlet-mapping>
  10. <servlet-name>helloService</servlet-name>
  11. <url-pattern>/hessian/helloService</url-pattern>
  12. </servlet-mapping>

从上述实例中。我们看出HessianServlet须要一个重要的初始化參数“service-class”,它的值必须是一个良好的“远程接口”实现类(默认构造器)。

HessianSkeleton和Client端的HessianProxy相应,它是负责server端Http请求的核心类,每一个HessianServlet都会持有一个HessianSkeleton实例,这个实例持有“service-class”对象。并在初始化时通过反射机制将“service-class”的全部方法列表放在一个methodMap中,key为“方法名”,value为Method(java.lang.reflect.Method),同一时候为了避免方法重载。还会额外的将“方法名_參数类型”作为一个新key。也放入methodMap中。

在HessianServlet初始化时,会通过反射机制的方式创建一个“service-class”的实例,当Http请求到达Servlet时,HessianSkeleton实例负责从请求中解析出“方法名”和參数列表;那么到此为止。一切就非常清楚了,HessianSkeleton依据方法名,从methodMap中获取Method,并调用其invode方法。然后将运行结果反序列化。写入Response。

1) Hessian的client每次调用,都会开启一个新的http链接,所以各个调用之间无法共享数据,其实你能够使用cookie技术来保存相关数据。可是其实收效甚微。

2) Hessian的client使用了java的动态代理,由于client须要明白知道接口的API信息。

3) Hessian的接口API中,不管是方法的參数还是方法的返回值,都必须是可序列化的(实现Serializable接口),你能够通过重写Serializable接口的相关方法。来自己定义序列化/反序列化操作。

4) 不要尝试使用hessian交互大数据。

5) 不要尝试对hessian的交互数据的二进制流进行额外的加密,这是一件得不偿失的事情,尽管看起来安全,其实收效甚微。

 
二.http请求与“幂等性”:

所谓“幂等性”。能够简单理解为:就是对一个资源使用同样的參数进行訪问,每次获得的结果应该是同样的。或者说“资源内容的变更”是符合预期的(比方数字自增操作);当中http get请求觉得是“幂等”的。可是http中通过post请求的方式导致server端数据变更,在某些情况下,可能打破“幂等性”。比方对server端的商品数量进行“减法”操作,假设请求发送成功,server运行成功,可是网络异常导致client未能受到结果。假设此时client重试,将会导致server端再次运行,可是其实。数量被反复计算了一次。

那么对与http-webservice而言,我们须要注意这一点,对与数据变更的接口调用,要么在接口中进行合理的逻辑校验。要么使用类似于“二阶段”提交的方式来做控制:

1) 首先发送一次请求,获取version,此version可能是数据库的乐观锁的version。也可能是redis/zookeeper等数据中心的一个唯一值。比方,我们在订单表中,每一个orderId都相应一个version。每次数据操作都会导致version++;那么在hessian接口中须要改动订单信息时,首先获取此version。

2) client传递须要变更的信息,同一时候也交付1)中获取的version。那么server端运行数据变更时,首先检測version是否一致,假设一致则改动,否则响应给client一个“重试”信号,那么client须要又一次获取version。然后继续提交。

三.核心API:

1) HessianProxy

2) HessianProxyFactory

3) HessianInput:输入流控制。用来反序列化响应的结果,当中包含remote端的异常栈(在client端将会被又一次抛出),“Fault”信息(remote端的失败信息,比方格式错误等)。非常多时候。你能够通过指定http response-code值来实现特定的请求失败信号。

4) HessianOutput:输出流控制。用来序列化请求的数据

5) Deserializer:反序列化接口,大量的实现类用于反序列化不同类型的数据

6) Serializer:序列化接口

四.程序实例(基于spring MVC):

    1. web.xml(Server端)

  1. <servlet>
  2. <servlet-name>hessianService</servlet-name>
  3. <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
  4. <init-param>
  5. <param-name>contextConfigLocation</param-name>
  6. <param-value>classpath:spring-mvc-servlet.xml</param-value>
  7. </init-param>
  8. </load-on-startup>
  9. </servlet>
  10. <servlet-mapping>
  11. <servlet-name>hessianService</servlet-name>
  12. <url-pattern>/hessian/*</url-pattern>
  13. </servlet-mapping>

这里有个奇怪的问题。假设hessian是通过spring公布的,那么url-pattern须要以“/hessian”前缀开头。

     2. spring-mvc-servlet.xml(Server端)

  1. <?

    xml version="1.0" encoding="UTF-8"?>

  2. <beans xmlns="http://www.springframework.org/schema/beans"
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4. xsi:schemaLocation="
  5. http://www.springframework.org/schema/beans
  6. http://www.springframework.org/schema/beans/spring-beans-3.0.xsd" default-autowire="byName">
  7. <bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"/>
  8. <import resource="hessian-servlet.xml"/>
  9. </beans>

autowire须要为“byName”。并且须要指定mapping方式为“BeanNameUrlHandlerMapping”。那么有spring公布的hessian服务。其beanName就能够作为servlet url的一部分。

    3. hessian-servlet.xml(Server端)

  1. <bean id="helloService" class="com.test.remote.impl.HelloServiceImpl" />
  2. <bean name="/userService" class="org.springframework.remoting.caucho.HessianServiceExporter">
  3. <property name="service" ref="userService"/>
  4. <property name="serviceInterface" value="com.test.remote.HelloService"/>
  5. </bean>

    4. client服务配置

  1. <!-- autowire: byName -->
  2. <bean id="helloService" class="org.springframework.remoting.caucho.HessianProxyFactoryBean">
  3. <property name="serviceUrl" value="http://localhost:8080/hessian/helloService" />
  4. <property name="serviceInterface" value="com.test.remote.HellowService" />
  5. <property name="overloadEnabled" value="true"/>
  6. </bean>

此后,开发人员就能够像使用其它spring bean一样使用helloService。

Hessian原理与程序设计的更多相关文章

  1. Hessian 原理分析

    Hessian 原理分析 一.远程通讯协议的基本原理 网络通信需要做的就是将流从一台计算机传输到另外一台计算机,基于传输协议和网络 IO 来实现,其中传输协议比较出名的有 http . tcp . u ...

  2. Hessian 原理分析--转

    原文地址:http://blog.csdn.net/zhtang0526/article/details/4788879 一.      远程通讯协议的基本原理 网络通信需要做的就是将流从一台计算机传 ...

  3. Hessian原理分析

    一.      远程通讯协议的基本原理 网络通信需要做的就是将流从一台计算机传输到另外一台计算机,基于传输协议和网络 IO 来实现,其中传输协议比较出名的有 http . tcp . udp 等等, ...

  4. hessian原理解析一(客户端分析)

    hessian 是一款开源的二进制远程通讯协议,使用简单方法提供了RMI功能,主要用于面向对象的消息通信. 优点:跨平台.多语言支持.使用简单 缺点:传递复杂对象性能会下降,不适合安全性高的应用 一 ...

  5. hessian协议原理

    Hessian 原理分析 一.      远程通讯协议的基本原理 网络通信需要做的就是将流从一台计算机传输到另外一台计算机,基于传输协议和网络 IO 来实现,其中传输协议比较出名的有 http . t ...

  6. Hessian使用

    ps:以前在项目中用过hessian,但我仅停留在知道这个层面,后面也没有详细了解其中的原理.现在要写简历都不知道怎么写,自己挖的坑,跪着也要填平. Hessian的使用 这里先写下工程中的使用,有个 ...

  7. 第一章-第五题(你所在的学校有计算机科学专业和软件工程专业么?相关专业的教学计划和毕业出路有什么不同?阅读有关软件工程和计算机科学的区别的文章,谈谈你的看法。)--By 侯伟婷

    我所在的本科学校和研究生学校都有计算机科学专业和软件工程专业.具体的教学计划无从得到,所以此情况无从对比,但是我从本科教务处网站找到了计算机科学专业和软件工程专业有关专业方面的课程,现列表如下. 表格 ...

  8. C#源码500份

    C Sharp  短信发送平台源代码.rar http://1000eb.com/5c6vASP.NET+AJAX基础示例 视频教程 http://1000eb.com/89jcC# Winform ...

  9. [转载] 读《UNIX网络编程 卷1:套接字联网API》

    原文: http://cstdlib.com/tech/2014/10/09/read-unix-network-programming-1/ 文章写的很清楚, 适合初学者 最近看了<UNIX网 ...

随机推荐

  1. Spring整合Hibernate与Struts

    整合S2SH 一.导入jar包 Spring jar包 Hibernate jar包 Struts2 jar包 以上就是整合需要的所有jar包,当然其中有重复的包,(对比之后去掉版本低的就可以了,还有 ...

  2. HTML中使用JavaScript

    1.script中使用src引入外部js.注意:不能是<script />,必须是<script></script> 2.使用link引入外部js 3.使用scri ...

  3. 求中位数为K的区间的数目

    给定一个长为 $n$ 的序列和常数 $k$,求此序列的中位数为 $k$ 的区间的数量.一个长为 $m$ 的序列的中位数定义为将此序列从小到大排序后第 $\lceil m / 2 \rceil$ 个数. ...

  4. webpack命令

    webpack // 执行一次开发的编译 webpack -p // 针对发布环境编译(压缩代码) webpack -w(或--watch) // 进行开发过程持续的增量编译(飞快地!) webpac ...

  5. JavaScript内存分配

    1.栈内存和堆内存 栈内存为自动分配的内存空间,由系统自动释放堆内存是动态分配的内存,大小不固定,也不会自动释放 js的值类型直接分配在栈内存中,引用类型分配在堆内存中引用类型变量保存的是引用类型的指 ...

  6. 超简单(super)

    超简单(super) 题目描述 有一个n面的骰子,第i面的数是vi,朝上的概率是pi. 教室的最后一排有一个人,不停地抛这个骰子,直到某一面朝上了两次,就停止抛骰子,但他不知道所有朝上的面的数字的和的 ...

  7. php读写文件要加锁

    http://www.bubuko.com/infodetail-241753.html

  8. 控制cxGrid 主从表的明细只展开一个

    procedure TForm.ADetailDataControllerCollapsing( ADataController: TcxCustomDataController; ARecordIn ...

  9. Berkeley DB使用SecondKey给数据排序的实现方法

    Berkeley DB使用SecondKey给数据排序的 实现方法是本文我们主要要介绍的内容,在做项目的时候用到了nosql数据库BDB,借此机会研究了一下它的用法.它的官方示例和文档比较丰富,感觉比 ...

  10. Eclipse中的android项目前面有叹号 (转)

    问题描述:在Eclipse中导入一个项目,在项目名上有感叹号出现,基本上是由于build path的问题. 解决方法: 在项目上右击-->build path -> configure b ...