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

  

一 、hessian demo 示例:

  1、新建一个maven项目,包含3个模块 API 模块(远程接口,供客户端和服务端使用),客户端模块和服务端模块

  2、添加hessian依赖包

       在pom.xml 中添加hessian依赖

    <dependency>

      <groupId>com.caucho</groupId>

      <artifactId>hessian</artifactId>

      <version>4.0.38</version>

    </dependency>

  3、远程接口定义

    在hessian-api 模块定义接口

public interface HelloHessian {

      String sayHello(User user);

}

  4、服务端接口实现

在hessian-server 模块实现API 接口

  public class HelloHessianImpl implements HelloHessian {

     public String sayHello(User user) {

      String userName = user.getName();

      System.out.println("hello:"+userName);

      return userName;

    }

  }

  5、服务端配置远程服务地址

  hessian 是依赖web 容器 需在web.xml 中配置服务请求地址

  <servlet>

    <servlet-name>HelloHessian</servlet-name>

     <!-- RPC HessianServlet处理类 -->

     <servlet-class>com.caucho.hessian.server.HessianServlet</servlet-class>

    <init-param>

      <param-name>service-class</param-name>

       <!-- 远程服务实现类 -->

      <param-value>com.hessian.server.HelloHessianImpl</param-value>

     </init-param>

  </servlet>

  <servlet-mapping>

     <servlet-name>HelloHessian</servlet-name>

     <url-pattern>/HelloHessian</url-pattern>

   </servlet-mapping>

  6、客户端远程接口调用

    // 远程服务地址

String url = "http://localhost:8080/HelloHessian";

//通过hessian代理工程创建接口代理

HessianProxyFactory fatory = new HessianProxyFactory();

HelloHessian helloHessian = (HelloHessian)fatory .create(HelloHessian.class,url);

// 执行方法调用

helloHessian.sayHello(new User(10, "tiger"));

  7、运行demo

7.1、启动服务器

7.2、运行客户端程序

控制台打印 hello:tiger

二、hessian 工作流程

三、客户端源码分析

1、客户端请求时序图

2、客户端创建接口的代理类分析:

HessianProxyFactory factory = new HessianProxyFactory();

HelloHessian helloHessian = (HelloHessian)factory.create(HelloHessian.class,url)

helloHessian.sayHello(new User(10, "tiger"))

2.1 通过HessianProxyFactory hessian代理工程类 创建代理类HessianProxy 具体代码如下:

public Object create(Class<?> api, URL url, ClassLoader loader) {

    ......

    InvocationHandler handler = new HessianProxy(url, this, api);

    return Proxy.newProxyInstance(loader, new Class[] { api, HessianRemoteObject.class }, handler);

}

2.2  HessianProxy 源码分析:

代理类HessianProxy 实现了InvocationHandler 接口 invoke(Object proxy, Method method, Object[] args)

2.2.1  获取调用的方法名以及方法参数类型

    String methodName = method.getName();

2.2.2  equals、hashCode、getHessianType、getHessianURL、toString 本地调用

  if(is1.equals("hashCode") && conn.length == 0) {

     return new Integer(this._url.hashCode());

  }

  if(is1.equals("getHessianType")) {

    return proxy.getClass().getInterfaces()[0].getName();

  }

2.2.3  重载支持 重载处理 mangleName = sayHello_User // 方法名_参数1_参数2_XX

2.2.4  执行远程调用(建立http链接,设置http header 信息, 序列化方法和参数,执行http请求)

//建立http链接

URLConnection conn = this._factory.openConnection(this._url);

//设置http header 信息

this.addRequestHeaders(conn);

//序列化并输出流

AbstractHessianOutput out = this._factory.getHessianOutput(conn.getOutputStream());

out .call(mangleName , args)  // out .call("sayHello",new Object[]{user})

out .flush();

call 方法封装hessian 版本信息、调用的方法名称、参数信息,格式如下

'c' 一个调用方法编码的开始标识符

1和0 Hessian的版本号,各占1 byte

length 方法名称的长度,占2 byte

method_name 方法名称

[params] 参数序列化内容,此参数是可选的

'z' 一个调用方法编码的结束标识符

eg:

User user = new User(10,"tiger");

FileOutputStream fos = new FileOutputStream("out.txt");

HessianOutput out = new HessianOutput(fos);

out.call("sayHello",new Object[]{user});

用BinaryViewer 工具打开 out.txt ,查看内容:此处为16进制数表示

63 01 00 6D 00 08 73 61 79 48 65 6C 6C 6F 4D 74

00 07 76 6F 2E 55 73 65 72 53 00 03 61 67 65 49

00 00 00 0A 53 00 04 6E 61 6D 65 53 00 05 74 69

67 65 72 7A 7A

63为'c'字符的编码

01 00为Hessian协议版本

6D为'm'字符编码

00 08 为函数名称的长度,因为'sayHello’函数的长度为8

73 61 79 48 65 6C 6C 6F 即为'sayHello'函数名称的字符编码;

7A为'z'的字符编码

2.2.5  获取输入流并反序列

is = conn.getInputStream();

AbstractHessianInput in = _factory.getHessianInput(is);

Object value = in.readObject(method.getReturnType())

三、服务端源码分析

  我们在回头看看 web.xml 中 servlet 配置

  <servlet>

    <servlet-name>HelloHessian</servlet-name>

    <servlet-class>com.caucho.hessian.server.HessianServlet</servlet-class>

    <init-param>

      <param-name>service-class</param-name>

      <param-value>com.hessian.server.HelloHessianImpl</param-value>

    </init-param>

  </servlet>

  服务端HessianServlet 处理hessian 请求 ,HessianServlet 继承HttpServlet, 每个servlet有两个重要的方法 init 和 service

1、void init(ServletConfig config) 初始化远程服务实现类、远程服务接口类以及HessianSkeleton

a、初始化远程服务实现类, home-class 、service-class

if (getInitParameter("home-class") != null) {

  String className = getInitParameter("home-class");

   Class<?> homeClass = loadClass(className);

  _homeImpl = homeClass.newInstance();

  init(_homeImpl);

} else if (getInitParameter("service-class") != null) {

  String className = getInitParameter("service-class");

  Class<?> homeClass = loadClass(className);

  _homeImpl = homeClass.newInstance(); init(_homeImpl);

}

b 、初始化远程服务接口

if (getInitParameter("home-api") != null) {

   String className = getInitParameter("home-api");

   _homeAPI = loadClass(className);

} else if (getInitParameter("api-class") != null) {

   String className = getInitParameter("api-class");

  _homeAPI = loadClass(className);

} else if (_homeImpl != null) {

   _homeAPI = _homeImpl.getClass();

}

c 、初始化 HessianSkeleton

_homeSkeleton = new HessianSkeleton(_homeImpl, _homeAPI);

// 取服务类的方法

Method []methodList = apiClass.getMethods()

// 遍历 服务类的方法,放入_methodMap

for (int i = 0; i < methodList.length; i++) {

Method method = methodList[i];

Class []param = method.getParameterTypes();

String mangledName = method.getName() + "__" + param.length;

_methodMap.put(method.getName(), methodList[i]);

_methodMap.put(mangledName, methodList[i]);

_methodMap.put(mangleName(method, false), methodList[i]);

}

hessian 支持重载处理,关键点: 比如对于一个方法 public void sayHello(User user), 那么在_methodMap中会存放三个key sayHello sayHello_1, sayHello_User

2、service(ServletRequest request, ServletResponse response) 重写父类的service 方法

2.1、 检查请求方式是否为POST

if (! req.getMethod().equals("POST")) {

  res.setStatus(500); //  "Hessian Requires POST"

   return;

}

2.2、获取输入输出流、对象的序列化工厂 执行目标目标服务方法

ServletInputStream e = request.getInputStream();

ServletOutputStream os = response.getOutputStream();

SerializerFactory serializerFactory = this.getSerializerFactory();

hessianSkeleton.invoke(is, os, serializerFactory)

hessianServlet 处理请求时序图

HessianSkeleton invoke 方法解析:

  public void invoke(Object service,AbstractHessianInput in,AbstractHessianOutput out){

  //读取方法调用名

  String methodName = in.readMethod();

  int argLength = in.readMethodArgLength();

  Method method = this.getMethod(methodName + "_" + argLength);

   //读取调用方法参数

   Class[] args = method.getParameterTypes();

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

for(int i= 0; i < args.length; ++i) {

     values[i] = in.readObject(args[i]);

   }

  //执行目标方法调用

result = method.invoke(service, values)

//结果输出

out.writeReply(result);

out.close();

输出格式:

此处为16进制数表示

72 01 00 53 00 05 74 69 67 65 72 7A

r            s              t   i   g    e   r   z

 

hessian原理解析一(客户端分析)的更多相关文章

  1. Hessian 原理分析

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

  2. Request 接收参数乱码原理解析三:实例分析

    通过前面两篇<Request 接收参数乱码原理解析一:服务器端解码原理>和<Request 接收参数乱码原理解析二:浏览器端编码原理>,了解了服务器和浏览器编码解码的原理,接下 ...

  3. (转)Apache和Nginx运行原理解析

    Apache和Nginx运行原理解析 原文:https://www.server110.com/nginx/201402/6543.html Web服务器 Web服务器也称为WWW(WORLD WID ...

  4. android黑科技系列——应用市场省流量更新(增量升级)原理解析

    一.前言 最近在看热修复相关的框架,之前我们已经看过了阿里的Dexposed和AndFix这两个框架了,不了解的同学可以点击这里进行查看:Dexposed框架原理解析 和 AndFix热修复框架原理解 ...

  5. gpfdist原理解析

    gpfdist原理解析 前言:gpfdist作为批量向postgresql写入数据的工具,了解其内部原理有助于正确使用以及提供更合适的数据同步方案.文章先简要介绍gpfdist的整体流程,然后针对重要 ...

  6. Request 接收参数乱码原理解析二:浏览器端编码原理

    上一篇<Request 接收参数乱码原理解析一:服务器端解码原理>,分析了服务器端解码的过程,那么浏览器是根据什么编码的呢? 1. 浏览器解码 浏览器根据服务器页面响应Header中的“C ...

  7. Request 接收参数乱码原理解析一:服务器端解码原理

    “Server.UrlDecode(Server.UrlEncode("北京")) == “北京””,先用UrlEncode编码然后用UrlDecode解码,这条语句永远为true ...

  8. 开源磁力搜索爬虫dhtspider原理解析

    开源地址:https://github.com/callmelanmao/dhtspider. 开源的dht爬虫已经有很多了,有php版本的,python版本的和nodejs版本.经过一些测试,发现还 ...

  9. JSONP跨域的原理解析

    JavaScript是一种在Web开发中经常使用的前端动态脚本技术.在JavaScript中,有一个很重要的安全性限制,被称为“Same- Origin Policy”(同源策略).这一策略对于Jav ...

随机推荐

  1. Unity3d之剥离alpha通道

    unity中, 将图集的 alpha 通道剥离出来可减少包体大小和内存使用大小. 方法是将原来的一张 rgba 图分成一张 rgb 和一张 alpha 图,android上rgb和alpha图均采用e ...

  2. jvm内存设置及总结

    http://dmouse.iteye.com/blog/1264118 jvm内存设置及总结 博客分类: java相关 Application情况: 大量使用了内存缓存,应用负载量较大,900w/d ...

  3. ftp linux-500 OOPS问题解决-jooyong-ChinaUnix博客

    body { font-family: "Microsoft YaHei UI","Microsoft YaHei",SimSun,"Segoe UI ...

  4. 五、Hive

    一.Hive 1.1 Hive简介 1.2 Hive说明 1.3Hive的体系架构 来自为知笔记(Wiz)

  5. poj1328贪心中的区间问题

    题意:给定海岛个数.雷达半径以及各海岛坐标,求能覆盖所有海岛的最小雷达数. 思路:先对每个海岛求一个区间:即能覆盖它的所有雷达的圆心所构成的区间.然后对区间排序,定义一个最右点over,依次延伸ove ...

  6. 在阿里云ECS(CentOS6.5)上安装redis

    下载所需的redis 命令: wget http://download.redis.io/releases/redis-3.0.7.tar.gz 结果: 解压redis压缩文件: 命令: .tar.g ...

  7. STM32驱动DHT11温湿度传感器

    DHT11 是一款湿温度一体化的数字传感器.该传感器包括一个电阻式测湿元件和一个 NTC 测温元件,并与一个高性能 8 位单片机相连接.通过单片机等微处理器简单的电路连接就能够 实时的采集本地湿度和温 ...

  8. Intent的概念及应用(一)

    ------siwuxie095 1.显式Intent (1)先创建一个项目:LearnIntent,选择API:21 Android 5.0, 选择Empty Activity,完成 (2)创建一个 ...

  9. github的SSH配置如下

    Git是分布式的代码管理工具,远程的代码管理是基于SSH的,所以要使用远程的Git则需要SSH的配置. github的SSH配置如下: 一 . 设置Git的user name和email: $ git ...

  10. 安卓 异步线程更新Ui

    异步跟新UI: 1.handler+Thread(runnable):如果handler和Thread都写在了一个Java文件中,就不说了,如果runnable定义在了一个单独的类文件中,可以通过在构 ...