`
QING____
  • 浏览: 2250805 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

Hessian原理与程序设计

    博客分类:
  • JAVA
 
阅读更多

    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地址。

 

 

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

 

    上述代码样例中,HelloService表示远程接口API,其中URL中“helloService”表示调用的“服务名称”,这个“服务名称”有server端决定,所以Client需要首先知道所需服务的URL全路径。那么方法的调用数据,将会按照如下“序列”发送(俗称“字节码成帧”):

   

[“方法名称“的字节长度]["方法名称”字节序列][参数的个数]{[参数类型][”参数“的字节长度][”参数“的字节序列]...}
    比如调用sayHello(String message)方法,那么序列化的字节流格式可能为:

 

[8][sayHello][1]['S'][5]['hello']
//其中“8”表示“sayHello”方法名称为8个字节
//"1"表示参数的个数为1
//“S”表示参数的类型为“String”,hessian中定义了大量的简写字母,用来表示java数据类型
//“5”表示参数的字节个数为5
//"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端。 

 

proxyFactory.setOverloadEnabled(true);//默认为false

 

 

Server端:

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

 

 

<servlet>
	<servlet-name>helloService</servlet-name>
	<servlet-class>com.caucho.hessian.server.HessianServlet</servlet-class>
	<init-param>
		<param-name>service-class</param-name>
		<param-value>com.test.hessian.impl.HelloServiceImpl</param-value>
	</init-param>
</servlet>

<servlet-mapping>
	<servlet-name>helloService</servlet-name>
	<url-pattern>/hessian/helloService</url-pattern>
</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端)

<servlet>
	<servlet-name>hessianService</servlet-name>
	<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
	<init-param>
		<param-name>contextConfigLocation</param-name>
		<param-value>classpath:spring-mvc-servlet.xml</param-value>
	</init-param>
	<load-on-startup>1</load-on-startup>
</servlet>

<servlet-mapping>
	<servlet-name>hessianService</servlet-name>
	<url-pattern>/hessian/*</url-pattern>
</servlet-mapping>

    这里有个奇怪的问题,如果hessian是通过spring发布的,那么url-pattern需要以“/hessian”前缀开头。 

 

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

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="
           http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans-3.0.xsd" default-autowire="byName">
           
    <bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"/> 
    <import resource="hessian-servlet.xml"/>

</beans>

    autowire需要为“byName”,而且需要指定mapping方式为“BeanNameUrlHandlerMapping”,那么有spring发布的hessian服务,其beanName就可以作为servlet url的一部分。

 

    3. hessian-servlet.xml(Server端)

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

 

    4. 客户端服务配置

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

    此后,开发者就可以像使用其他spring bean一样使用helloService。

分享到:
评论
2 楼 eliot4u 2014-09-04  
和SPring Mvc结合起来效果不错
1 楼 cnpoint 2014-04-29  
草哥V5  

相关推荐

    hessian

    Hessian是一种二进制Web服务协议,由Caucho Technology公司开发,主要用于提供轻量级、高效的远程方法调用(Remote Method ...通过深入研究源码和利用相关工具,我们可以更好地利用Hessian的优势来优化我们的应用程序。

    hessian-4.0.7jar文件与源代码.rar

    `hessian-4.0.7-src.jar`则是Hessian-4.0.7的源代码,开发者可以通过查看源代码来了解Hessian的工作原理,学习如何使用它来创建自定义的序列化和反序列化逻辑,或者对协议进行扩展。源代码还方便了调试和问题排查,...

    hessian服务端 客户端 可运行

    总的来说,"hessian服务端 客户端 可运行"的压缩包提供了一个方便的平台,让开发者能够快速了解和测试Hessian协议的工作原理。通过这个示例,你可以深入理解如何在Java环境中搭建和使用Hessian服务,以及如何在...

    Hessian应用

    虽然提供的部分内容没有给出具体的User类代码,但我们可以根据Hessian的一般使用场景来推测可能的User类设计。假设User类如下所示: ```java public class User { private String name; private int age; // ...

    C#中Hessian的使用例子

    首先,Hessian协议设计的目标是简洁高效,它能将常见的数据类型如字符串、整型、浮点型等转换为二进制格式,从而在网络传输时占用更少的带宽。这种协议特别适合于处理大量数据的Web服务或分布式应用。 在C#中,我们...

    DuBBo(rmi+Hessian).zip

    本压缩包“DuBBo(rmi+Hessian).zip”提供了关于Dubbo如何与RMI(Remote Method Invocation)和Hessian两种通信协议结合使用的深入学习资料。下面我们将详细探讨这两个知识点。 首先,RPC(Remote Procedure Call)...

    Flex中的Hessian

    Flex中的Hessian是一种二...通过深入理解Hessian的工作原理和配置,开发者可以构建出响应更快、资源利用率更高的Flex应用程序。在实际开发中,结合良好的服务设计和优化,Hessian能够为Flex应用带来显著的性能提升。

    最优化方法原理与matlab习题答案

    在"最优化方法原理与MATLAB习题答案"中,我们可以探讨以下几个关键知识点: 1. **最优化基础概念**:这包括目标函数和约束条件,无约束优化和有约束优化,以及全局最优解和局部最优解的概念。最优化问题通常可以...

    《马昌凤-最优化方法及其Matlab程序设计》_matlab_最优化基本算法_

    《马昌凤-最优化方法及其Matlab程序设计》是一本深入探讨最优化算法与MATLAB编程实践的著作。本书旨在帮助读者理解并掌握优化问题的解决策略,同时通过MATLAB这一强大的数值计算工具实现这些算法,提升实际操作能力...

    最优化方法及其Matlab程序设计,最优化方法及其matlab程序设计pdf,matlab

    《最优化方法及其Matlab程序设计》这本教材详细阐述了最优化理论及其在MATLAB环境下的实现。书中可能涵盖了线性规划、非线性规划、整数规划、动态规划、梯度法、牛顿法、拟牛顿法、遗传算法、粒子群优化、模拟退火法...

    Flex + Hessian 学习笔记(二)

    Flex和Hessian是一种常见的客户端-服务器通信技术组合,用于构建富互联网应用程序(RIA)。这篇学习笔记将深入探讨这两个技术,并通过实例代码解析它们的工作原理。 Flex是Adobe开发的一个开源框架,主要用于创建...

    nacos-hessian-rce.pdf

    - **Hessian 特性**: Hessian 支持跨语言特性,允许 Java 应用程序与其他语言编写的客户端进行通信。 - **反序列化攻击**: 如果没有正确配置白名单或者黑名单,任何类都可以被反序列化,这为攻击者提供了机会。 - **...

    外部接口调用 使用spring4+hessian4实例

    在这个场景中,Hessian4被用作一个轻量级的RPC(远程过程调用)协议,它允许应用程序在不同的Java虚拟机之间透明地调用方法。Spring4则扮演了管理和协调这些调用的角色,提供了一种优雅的方式去配置和管理服务。 ...

    最优化方法及其Matlab程序设计,最优化方法及其matlab程序设计pdf,matlab源码.zip

    该资料包“最优化方法及其Matlab程序设计”包含了两个主要部分:一本电子书和相关的Matlab源码。电子书可能详细介绍了各种最优化技术,包括线性规划、非线性规划、动态规划、整数规划、模拟退火、遗传算法、粒子群...

    Dogleg算法——信赖域算法

    Dogleg算法的名字来源于其迭代路径形状类似狗的拐腿,这个路径设计巧妙地结合了梯度下降和拟牛顿步长的优点,试图在最小化局部线性模型的同时避免过度偏离当前点。该算法主要由以下几步构成: 1. **初始步长选择**...

    rpc原理的简单实现

    RPC(Remote Procedure Call)远程过程调用是一种计算机通信协议,它允许程序在一台计算机上执行一个功能,并且看起来像在本地执行一样。RPC的核心思想是让客户端能够透明地调用远程服务器上的服务,无需关心底层...

    Dubbo RPC框架原理解析和源码

    RPC(Remote Procedure Call)允许一个程序调用另一个在不同网络计算机上的程序,就像调用本地函数一样简单。Dubbo的出现极大地简化了服务间的通信,提高了系统的可扩展性。 **一、Dubbo的基本架构** Dubbo的核心...

    Java远程通讯可选技术及原理

    传输协议是在Socket基础上针对特定应用场景设计的,而网络IO则提供了不同类型的I/O模型以优化数据传输效率。 RMI(Remote Method Invocation)是Java特有的远程过程调用协议,旨在提供类似本地对象调用的体验。RMI...

    最优化设计matlab程序

    本资料包聚焦于“最优化设计MATLAB程序”,涵盖了几种经典的优化方法,包括最速下降法、牛顿法和单纯形法。 **1. 最速下降法**: 最速下降法是一种梯度下降优化策略,用于寻找函数的局部最小值。在每一步迭代中,它...

Global site tag (gtag.js) - Google Analytics