学习笔记_Hessian

Hessian

如果读者想快速了解相关配置原理的,可以直接从3.3节开始浏览

1、hessian简介

1.1 hessian是什么

hessian是一个轻量级的remoting onhttp工具,使用简单的方法提供了RMI的功能。相比WebService,hessian更简单、快捷。采用的是二进制的RPC协议,因为采用的是二进制协议,所以它很适合于发送二进制数据。

1.2 hessian的优缺点

优点:

比 Java 原生的对象序列化/反序列化速度更快,,序列化出来以后的数据更小。序列化协议跟应用层协议无关, 可以将 Hessian 序列化以后的数据放在 HTTP Body 里,,也可以放在 DUBBO 里,,或者直接用 Socket 传输。Hessian协议和web service常用的SOAP协议类似,也是将协议报文封装在HTTP封包中,通过HTTP信道进行传输的。因此Hessian协议具有与SOAP协议同样的优点——传输不受防火墙的限制(防火墙通常不限制HTTP信道),不需要配置防火墙

Hessian类似于Webservice,但是它不使用soap协议,它把协议报文封装到http封包中,通过HTTP信道传输。是一种高效简洁的远程调用框架,它采用的是二进制RPC协议(Binary),具有轻量、传输量小、平台无关的特点,特别适合于目前网络带宽比较小的手机网络应用项目。简单易用,面向接口,通过接口暴露服务,jar包只有200、300k,效率高,复杂对象序列化速度仅次于RMI,简单对象序列化优于RMI,二进制传输多语言支持。为什么序列化后数据更小呢?因为:

它把本地格式的数据编码为二进制数据,仅用一个字符作为结构化标记,HBWSP封装后的数据增量明显小于SOAP封装后的数据增量。并且相对于SOAP,Hessian协议的外部数据表示有3个显著的优势:

  • 采用简单的结构化标记。简单的结构化标记减少了编码、解码操作对内存的占用量。编码时,只需写少量的数据,就可以标记结构;解码时,只需读少量的数据就可以确定结构。而且,简单的结构化标记减少了编码后的数据增量。
  • 采用定长的字节记录值。用定长的字节记录值,解码时,就可以使用位操作从固定长度的位获得值。这样不仅操作简单,而且可以获得较高的性能。
  • 采用引用取代重复遇到的对象。使用引用取代重复遇到的对象可以避免对重复对象的编码,而且也减少了编码后的数据量。

因此使用Hessian协议传输数据量比SOAP协议要小得多。实践证明,传输同样的对象Hessian协议传输的数据量比SOAP协议低一个数量级。因此Hessian协议比SOAP协议更适用于分布式应用系统间大数据量的数据交换

Hessian是通过servlet提供远程服务,完全使用动态代理来实现的,推荐采用面向接口编程,因此,Hessian服务建议通过接口暴露。hessian已经支持Java,Flash/Flex,Python,C++,.NET C#,D,Erlang,PHP,Ruby,Objective C。

缺点

如果service层中返回的对象是复杂对象,使用它就会削弱Hessian的传输量小的优点,而且也会增加Hessian客户端的代码量。既然它是把对象序列化为二进制流的形式在http信道中传输,那么对于安全性高的应用不应该采用hessian(比如网上支付等)。缺乏安全机制,传输没有加密处理, 异常机制不完善,总是报一些错误,错误原因也是千奇百怪,提示信息不足, 事务处理欠缺, 版本问题,spring 2.5.6对照3.1.3版,spring 3对照4.0及以上版本,需要使用spring MVC。

关于hession和其他通讯RPC方式的一些比较

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

  2. 和RMI,HTTPINvoker等对比

    通讯效率测试结果:
    RMI > Httpinvoker >= Hessian >> Burlap >> Web service

    1. RMI 是 Java 首选远程调用协议,非常高效稳定,特别是在数据结构复杂,数据量大的情况下,与其他通讯协议的差距尤为明显。但不能跨语言
    2. HttpInvoker 使用 java 的序列化技术传输对象,与 RMI 在本质上是一致的。从效率上看,两者也相差无几, HttpInvoker 与 RMI 的传输时间基本持平。
    3. Hessian 在传输少量对象时,比 RMI 还要快速高效,但传输数据结构复杂的对象或大量数据对象时,较 RMI 要慢 20% 左右。但这只是在数据量特别大,数据结构很复杂的情况下才能体现出来,中等或少量数据时, Hessian并不比RMI慢。 Hessian 的好处是精简高效,可以跨语言使用,而且协议规范公开,我们可以针对任意语言开发对其协议的实现。另外, Hessian与WEB服务器结合非常好,借助WEB服务器的成熟功能,在处理大量用户并发访问时会有很大优势,在资源分配,线程排队,异常处理等方面都可以由成熟的WEB服务器保证。而 RMI 本身并不提供多线程的服务器。而且,RMI 需要开防火墙端口, Hessian 不用。
    4. Burlap 采用 xml 格式传输。仅在传输 1 条数据时速度尚可,通常情况下,它的毫时是 RMI 的 3 倍。
    5. Web Service 的效率低下是众所周知的,平均来看, Web Service 的通讯毫时是 RMI 的 10 倍。

1.3 关于hessian的7个问题

  1. 是基于什么协议实现的?
    基于Binary-RPC协议实现。
  2. 怎么发起请求?
    需通过Hessian本身提供的API来发起请求。
  3. 怎么将请求转化为符合协议的格式的?
    Hessian通过其自定义的串行化机制将请求信息进行序列化,产生二进制流。
  4. 使用什么 传输协议传输?
    Hessian基于Http协议进行传输。
  5. 响应端基于什么机制来接收请求?
    响应端根据Hessian提供的API来接收请求。
  6. 怎么将流还原为传输格式的?
    Hessian根据其私有的串行化机制来将请求信息进行反序列化,传递给使用者时已是相应的请求信息对了。
  7. 处理完毕后怎么回应?
    处理完毕后直接返回,hessian将结果对象进行序列化,传输至调用端。

2、RPC框架

2.1 RPC简介

RPC是指远程过程调用,也就是说两台服务器A,B,一个应用部署在A服务器上,想要调用B服务器中应用提供的函数/方法,由于不在一个内存空间,不能直接调用,需要通过网络来表达调用的语法和传达调用的数据。

比如,一个方法如下定义:

Employee getEmployeeByName(String fullName)

那么:

  1. 要解决通讯问题,主要是通过在客户端和服务器之间建立TCP连接,远程过程调用的所有交换的数据都会在这个连接里传输。连接可以是按需连接,调用结束后就断掉,也可以是长连接,多个远程调用共享同一个连接。
  2. 要解决寻址问题,也就是说,A服务器上的应用要怎么告诉底层的RPC框架,如何连接到B服务器(如主机或IP地址)以及特定的端口,方法的名称。这样才能完成调用。比如基于Web服务协议栈的RPC,就要提供一个endpoint URI,或者从UDDI服务上查找。如果是RMI调用的话,还需要一个RMI Registry来注册服务的地址。
  3. 当A服务器上的应用发起远程过程调用时,方法的参数需要通过底层的网络协议如TCP传递到B服务器,由于网络协议是基于二进制的,内存中的参数的值要序列化成二进制的形式,也就是序列化(Serizlize)或编组(marshal),通过寻址和传输将序列化的二进制发送给B服务器。
  4. B服务器收到请求后,需要对参数进行反序列化,恢复为内存中的表达方式,然后找到对应的方法(寻址的一部分)进行本地调用,然后得到返回值。
  5. 返回值还要发送回服务器A上的应用,也要经过序列化的方式发送,服务器A接到后在反序列化,恢复为内存中的表达方式,交给A服务器上的应用。

preview

2.2 为什么需要RPC

现在的应用程序的功能和模块无法在一台服务器上完成,为了提高效率,采用分布式的方式进行布局已然成为主流。在不同系统间的通讯,甚至是不同组织间的通讯,就需要通过PRC来实现。

RPC的协议有很多,比如最早的CORBA, JAVA RMI, Web Service的RPC风格,hessian, Thrift 甚至Rest API

3、hessian基本使用

导入实例代码

项目目录结果如下:

image-20210625154232902

api:客户端和服务端均共同引用的接口api

hessianclient:客户端调用,包括本地直接通过代理对象以及Spring集成方式调用

hessianserver:通过传统servlet方式提供服务

hessianserver-spring:与Spring集成提供服务

创建普通maven项目hessian,删除其中的src目录项。将整个项目作为父项目,然后根据需要创建子项目即可

可以把一些公共的依赖放在父目录下的pom.xml中

<dependencies>
    <!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <version>1.18.20</version>
        <scope>provided</scope>
    </dependency>


    <!-- https://mvnrepository.com/artifact/com.caucho/hessian -->
    <dependency>
        <groupId>com.caucho</groupId>
        <artifactId>hessian</artifactId>
        <version>4.0.65</version>
    </dependency>

</dependencies>

3.1 新建maven项目api

image-20210625154750228

接口IHello

package top.saodisheng;

import top.saodisheng.entity.User;

import java.util.List;
import java.util.Map;

/**
 * Description:
 *
 * @author 扫地生_saodisheng
 * @date 2021/06/25
 */
public interface IHello {
    public String sayHello(String name);
    public String getUserList(List<User> users);
    public String getUserMap(Map<String, User> maps);
}

实体类User:

package top.saodisheng.entity;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.io.Serializable;

/**
 * Description:
 *
 * @author 扫地生_saodisheng
 * @date 2021/06/25
 */
@Data
@AllArgsConstructor@NoArgsConstructor
public class User implements Serializable {
    private static final long serialVersionUID = 1L;

    private String userName;
    private String password;

}

3.2 通过传统的servlet实现RPC

服务端

1、新建maven web项目hessianserver

image-20210625155230362

2、导入依赖

<dependencies>
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.11</version>
        <scope>test</scope>
    </dependency>

    <!--导入api-->
    <dependency>
        <groupId>top.saodisheng</groupId>
        <artifactId>hessianapi</artifactId>
        <version>1.0-SNAPSHOT</version>
        <scope>compile</scope>
    </dependency>
</dependencies>

3、创建api公共接口对应的实现类IHelloImpl

package top.saodisheng.impl;

import top.saodisheng.IHello;
import top.saodisheng.entity.User;

import java.util.List;
import java.util.Map;

/**
 * Description:
 *
 * @author 扫地生_saodisheng
 * @date 2021/06/25
 */
public class IHelloImpl implements IHello {
    @Override
    public String sayHello(String name) {
        return "Hello " + name;
    }

    @Override
    public String getUserList(List<User> users) {
        StringBuffer userString = new StringBuffer();
        for (User user : users) {
            userString.append(user.toString());
        }

        return userString.toString();
    }

    @Override
    public String getUserMap(Map<String, User> maps) {
        StringBuffer userString = new StringBuffer();
        for (Map.Entry e : maps.entrySet()) {
            userString.append(e.getValue());
        }
        return userString.toString();
    }
}

4、配置Tomcat

image-20210625155458772

5、web.xml中配置hessian Servlet

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
          http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0">

<!--  hessian-->
  <servlet>
    <servlet-name>Hello</servlet-name>
    <servlet-class>com.caucho.hessian.server.HessianServlet</servlet-class>
    <init-param>
      <param-name>home-class</param-name>
      <param-value>top.saodisheng.impl.IHelloImpl</param-value>
    </init-param>
    <init-param>
      <param-name>home-api</param-name>
      <param-value>top.saodisheng.IHello</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
  </servlet>

  <servlet-mapping>
    <servlet-name>Hello</servlet-name>
    <url-pattern>/Hello</url-pattern>
  </servlet-mapping>
</web-app>

客户端

1、新建普通maven项目hessianclient

image-20210625155631105

2、添加依赖

<dependencies>
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.11</version>
        <scope>test</scope>
    </dependency>

    <!--导入api-->
    <dependency>
        <groupId>top.saodisheng</groupId>
        <artifactId>hessianapi</artifactId>
        <version>1.0-SNAPSHOT</version>
        <scope>compile</scope>
    </dependency>
    
</dependencies>

3、编写测试类

package test;

import com.caucho.hessian.client.HessianProxyFactory;
import top.saodisheng.IHello;
import top.saodisheng.entity.User;

import java.net.MalformedURLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * Description:
 * 传统的servlet方法调用
 * @author 扫地生_saodisheng
 * @date 2021/06/25
 */
public class ClientTest {
    public static String url = "http://127.0.0.1:8080/Hello";

    public static void main(String[] args) {
        HessianProxyFactory factory = new HessianProxyFactory();
        try {
            // 从代理工厂中生成一个实例
            IHello iHello = (IHello) factory.create(IHello.class, url);

            // 模拟数据
            User user1 = new User("saodisheng", "123");
            User user2 = new User("扫地生", "321");
            List<User> userList = new ArrayList<>();
            userList.add(user1);
            userList.add(user2);

            Map<String, User> userMap = new HashMap<>();
            userMap.put("user1", user1);
            userMap.put("user2", user2);

            // 调用远程方法,看看是否正常工作
            System.out.println(iHello.sayHello("saodisheng"));

            System.out.println(iHello.getUserList(userList));

            System.out.println(iHello.getUserMap(userMap));

        } catch (MalformedURLException e) {
            e.printStackTrace();
        }
    }
}

4、启动服务器,测试

image-20210625160037733

image-20210625160105942

3.3 hessian配置说明

hessian是实现webservice的一个框架,
hessian传输是http协议之上应用层协议(hessian自定义二进制rpc协议)。
hessian传输小数量数据速度很快。

使用hessian+spring开发webservice的流程:

服务端:
            1. 编写dao,service方法
            2. 使用spring容器发布服务,
            3. 在web.xml配置hessian的servlet
下面是spring配置文件中发布服务接口的方法
<!-- hessian服务
HessianServiceExporter:将service指定的bean生成hessian服务接口
service:提供hessian接口服务的bean
serviceInterface:Hessian服务的接口
 -->
<bean name="/ypxxRemoteService"
class="org.springframework.remoting.caucho.HessianServiceExporter">
<property name="service" ref="ypxxService" />
<property name="serviceInterface">
<value>
cn.xxxx.yycg.hessian.server.YpxxService
</value>
</property>
</bean>
------<!-- hessian和spring整合,配置hessian servlet -->
    <servlet>  
   <servlet-name>Hessian</servlet-name>  
    <servlet-class>  
        org.springframework.web.servlet.DispatcherServlet   
   </servlet-class>  
   <!-- hessian-service.xml配置hessian服务信息 -->
   <init-param>
   <param-name>contextConfigLocation</param-name>
   <param-value>classpath:hessian/hessian-service.xml</param-value>
   </init-param>
    <load-on-startup>1</load-on-startup>  
</servlet>
<servlet-mapping>
<servlet-name>Hessian</servlet-name>
<!-- hessian的接口地址=http://ip:port/hessian/hessian-service.xml文件中配置的地址 -->
<url-pattern>/hessian/*</url-pattern>
</servlet-mapping>

客户端:
1.使用spring容器创建service的代理对象, 首先把服务端接口及相关的类拷贝到客户端工程,
注意:包名与服务端包名必须一致
<!-- 配置客户端存根 代理对象 -->
<bean id="ypxxServiceProxy" class="org.springframework.remoting.caucho.HessianProxyFactoryBean">
<!-- 配置代理对象的类型即服务端接口类型UserService -->
<property name="serviceInterface">
<value> cn.xxxx.yycg.hessian.server.YpxxService</value>
</property>
<!-- 配置调用服务端接口地址 -->
<property name="serviceUrl">
<value> http://localhost:8080/yycg_sheng/hessian/ypxxRemoteService
</value>
</property>
</bean>
2.在客户端调用服务的方法中注入代理对象的实例,通过代理对象调用方法.

3.4 通过Spring集成hessian实现RPC、

服务端

1、新建maven web项目hessianserver

image-20210625160526974

2、导入依赖

<dependencies>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.11</version>
            <scope>test</scope>
        </dependency>

        <!--导入api-->
        <dependency>
            <groupId>top.saodisheng</groupId>
            <artifactId>hessianapi</artifactId>
            <version>1.0-SNAPSHOT</version>
            <scope>compile</scope>
        </dependency>

        <!-- spring -->
        <!-- https://mvnrepository.com/artifact/org.springframework/spring-web -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-web</artifactId>
            <version>5.3.8</version>
        </dependency>

        <!--spring mvc-->
        <!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>5.3.8</version>
        </dependency>

    </dependencies>

3、创建api公共接口对应的实现类IHelloImpl

package top.saodisheng.impl;

import top.saodisheng.IHello;
import top.saodisheng.entity.User;

import java.util.List;
import java.util.Map;

/**
 * Description:
 *
 * @author 扫地生_saodisheng
 * @date 2021/06/25
 */
public class IHelloImpl implements IHello {
    @Override
    public String sayHello(String name) {
        return "Hello " + name;
    }

    @Override
    public String getUserList(List<User> users) {
        StringBuffer userString = new StringBuffer();
        for (User user : users) {
            userString.append(user.toString());
        }

        return userString.toString();
    }

    @Override
    public String getUserMap(Map<String, User> maps) {
        StringBuffer userString = new StringBuffer();
        for (Map.Entry e : maps.entrySet()) {
            userString.append(e.getValue());
        }
        return userString.toString();
    }
}

4、配置Tomcat

image-20210625160607389

5、applicationContext.xml中配置hessian服务

<?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-4.1.xsd"
       default-lazy-init="true">
    <!--注入提供服务的bean-->
    <bean name="helloService" class="top.saodisheng.impl.IHelloImpl"></bean>
    <!--
        HessianServiceExport:将service指定的bean生成hessian服务接口
        service:提供hessian接口服务的bean
        serviceInterface:hessian服务的接口
    -->
    <bean name="/helloRemoteService" class="org.springframework.remoting.caucho.HessianServiceExporter">
        <property name="service" ref="helloService"></property>
        <property name="serviceInterface">
           <value>top.saodisheng.IHello</value>
        </property>
    </bean>

</beans>

6、webt.xml中配置整合Spring和hessian

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
          http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0">
  <!-- hessian和spring整合,配置hessian servlet -->
  <servlet>
    <servlet-name>hello</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <!-- applicationContext.xml配置hessian服务信息 -->
    <init-param>
      <param-name>contextConfigLocation</param-name>
      <param-value>classpath:applicationContext.xml</param-value>
    </init-param>

    <load-on-startup>1</load-on-startup>
  </servlet>

  <servlet-mapping>
    <servlet-name>hello</servlet-name>
    <!-- hessian的接口地址 = http://ip:prot/saodishengRemoteService/applicationContext.xml文件中配置的地址-->
    <url-pattern>/saodishengRemoteService/*</url-pattern>
  </servlet-mapping>
</web-app>

客户端

1、在hessianclient项目中添加依赖,添加后依赖如下:

<dependencies>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.11</version>
            <scope>test</scope>
        </dependency>

        <!--导入api-->
        <dependency>
            <groupId>top.saodisheng</groupId>
            <artifactId>hessianapi</artifactId>
            <version>1.0-SNAPSHOT</version>
            <scope>compile</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.3.8</version>
            <scope>compile</scope>
        </dependency>

        <!-- spring -->
        <!-- https://mvnrepository.com/artifact/org.springframework/spring-web -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-web</artifactId>
            <version>5.3.8</version>
        </dependency>

        <!--spring mvc-->
        <!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>5.3.8</version>
        </dependency>

        <!-- https://mvnrepository.com/artifact/org.springframework/spring-beans -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-beans</artifactId>
            <version>5.3.8</version>
        </dependency>

    </dependencies>

2、编写客户端配置文件applicationContext-hessian.xml

<?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-4.1.xsd"
       default-lazy-init="true">
    <!-- 配置客户端存根 代理对象-->
    <bean name="helloServiceProxy" class="org.springframework.remoting.caucho.HessianProxyFactoryBean">
        <!-- 配置代理对象的类型即服务端对应接口类型-->
        <property name="serviceInterface">
            <value>top.saodisheng.IHello</value>
        </property>
        <!-- 配置服务端暴露出来的接口地址-->
        <property name="serviceUrl">
            <value>http://localhost:8080/saodishengRemoteService/helloRemoteService</value>
        </property>
    </bean>
</beans>

3、编写测试类

package test;

import org.springframework.context.support.ClassPathXmlApplicationContext;
import top.saodisheng.IHello;
import top.saodisheng.entity.User;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * Description:
 * 整合Spring调用远程方法
 * @author 扫地生_saodisheng
 * @date 2021/06/25
 */
public class Client_springTest {
    public static void main(String[] args) {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext-hessian.xml");
        // 获取实例
        IHello helloService = (IHello)context.getBean("helloServiceProxy");

        // 模拟数据
        User user1 = new User("saodisheng", "123");
        User user2 = new User("spring", "321");
        List<User> userList = new ArrayList<>();
        userList.add(user1);
        userList.add(user2);

        Map<String, User> userMap = new HashMap<>();
        userMap.put("user1", user1);
        userMap.put("user2", user2);

        // 调用远程方法,看看是否正常工作
        System.out.println(helloService.sayHello("saodisheng"));

        System.out.println(helloService.getUserList(userList));

        System.out.println(helloService.getUserMap(userMap));
    }
}

4、启动服务器,测试

image-20210625154405042

image-20210625154532566

4、探究hessian实现细节

回到文章开始提出的问题,即pc框架需要解决的问题,看看hessian如何解决的。

4.1 通讯问题:

我们先看看客户端在发起远程请求前都经历了什么:

首先,客户端通过代理工厂对象HessianProxyFactory的create方法创建代理对象;

image-20210625161750633

我们跟踪进入create方法里可以看到,该代理对象在执行时的handler是通过HessianProxy代理对象的invoke方法来执行;典型的动态代理;

image-20210625161933091

image-20210625162242726

在invoke方法中:

通过String methodName = method.getName();得到方法名

image-20210625162451735

通过sendRequest方法取得和服务端的连接HessianConnection对象;

image-20210625162546190

跟踪sendRequest方法,我们发现:

img

发现,hessian是使用http协议进行网络通信;

在is = getInputStream(conn);处等待服务端返回的响应;

4.2 寻址问题

我们跟踪这里:

image-20210625163256684

image-20210625163522749

image-20210625163611548

image-20210625163651609

我们得出结论:hessian使用lookup方法来寻找远程服务;

4.3 序列化与反序列化

我们继续看刚刚跟踪的客户端调用时执行的HessianProxy对象的invoke方法,

进入其中的

conn = sendRequest(mangleName, args);

再进入

out.call(methodName, args);

再进入

writeObject(args[i]);

进入其Hessian2Output实现中

img

最终看到了hessian如何进行序列化:

serializer.writeObject(object, this);

img

至此,我们也可以梳理一下hessian客户端动态代理的执行流程:img

我们再来看看服务端的执行细节:

通过后台的代码,可见我们所有的工作都围绕在HessianServlet在展开。该Servlet中有两个比较重要的方法:init()、service();

init方法初始化服务和服务对象,主要分为3步:

通过home-class或者service-class创建服务端的实现类实例;

init方法还会创建HessianSkeleton对象,这是Hessian服务端的核心功能部分。

HessianSkeleton继承自AbstractSkeleton,其构造方法,将会从实现类中抽取方法和方法的Method对象,并且存储到_methodMap中。

对于一个Servlet来说其service方法是对外提供服务的方法:

最主要的是调用HessianSkeleton对象的invoke方法。注意,Servlet实例中有两个HessianSkeleton变量,分别是:_objectSkeleton和 _homeSkeleton,

invoke方法:

首先从HessianInput对象中获取到Method信息,获取到真正的service对象。

根据反射机制,调用service对象的invoke方法,获取到返回值。

最后调用HessianOutput对象将结果写回到调用方。

img

img

参考文章1

参考文章2

参考文章3

参考文章4

posted @ 2021-06-25 16:42  技术扫地生—楼上老刘  阅读(554)  评论(0编辑  收藏  举报