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

    向大神看齐
  • 相关阅读:
    牛客IOI周赛17-提高组 卷积 生成函数 多项式求逆 数列通项公式
    6.3 省选模拟赛 Decompose 动态dp 树链剖分 set
    AtCoder Grand Contest 044 A Pay to Win 贪心
    5.29 省选模拟赛 树的染色 dp 最优性优化
    luogu P6097 子集卷积 FST FWT
    CF724C Ray Tracing 扩展欧几里得 平面展开
    5.30 省选模拟赛 方格操作 扫描线 特殊性质
    5.29 省选模拟赛 波波老师 SAM 线段树 单调队列 并查集
    Spring main方法中怎么调用Dao层和Service层的方法
    Bug -- WebService报错(两个类具有相同的 XML 类型名称 "{http://webService.com/}getPriceResponse"。请使用 @XmlType.name 和 @XmlType.namespace 为类分配不同的名称。)
  • 原文地址:https://www.cnblogs.com/Liu-xing-wu/p/14931594.html
Copyright © 2011-2022 走看看