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

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了hessian原理解析一(客户端分析)相关的知识,希望对你有一定的参考价值。

  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())

 

 

以上是关于hessian原理解析一(客户端分析)的主要内容,如果未能解决你的问题,请参考以下文章

Hessian原理分析

Dubbo序列化原理

Redis集群故障监测及哨兵机制原理解析

SpringBoot2各类型参数解析原理(源码分析)

Spring Cloud Ribbon原理解析

React 服务器渲染原理解析与实践