zoukankan      html  css  js  c++  java
  • 学习笔记_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

    向大神看齐
  • 相关阅读:
    王颖奇 201771010129《面向对象程序设计(java)》第七周学习总结
    王颖奇 201771010129《面向对象程序设计(java)》第六周学习总结
    王颖奇 201771010129《面向对象程序设计(java)》第四周学习总结
    王颖奇 201771010129 第三周 Java基本程序设计总结
    《三带一队》【Alpha】Scrum meeting 3
    《三带一队》【Alpha】Scrum meeting 2
    《三带一队》【Alpha】Scrum meeting 1
    三带一队 实验八 团队作业4:团队项目需求建模与系统设计
    三带一队 实验七 团队作业3:团队项目需求分析与原型设计
    三带一队 实验六团队作业2:麋鹿寻途
  • 原文地址:https://www.cnblogs.com/Liu-xing-wu/p/14931594.html
Copyright © 2011-2022 走看看