• Hession实现远程通讯(基于Binary-RPC协议)


    一、开发工具

    1.jdk1.6 64位

    百度网盘地址:https://pan.baidu.com/s/1Zwqfmi20X4ANNswZzPMzXQ 提取码:k50r

    2.apache-maven-3.2.5

    百度网盘地址:https://pan.baidu.com/s/1b9ZEnVclXhllmiCoVc3vyQ 提取码:x8jx

    3.Eclipse IDE 4.11.0

    百度网盘地址:https://pan.baidu.com/s/14_aDA2-xJpQBpDDtDZ_Sag 提取码:5abt

    4.apache-tomcat-7.0.68

    百度网盘地址:https://pan.baidu.com/s/1SFxj-l8rHpV4e091cT4vGw 提取码:w83x

    二、远程通讯协议的基本原理

      网络通信需要做的就是将流从一台计算机传输到另外一台计算机,基于传输协议和网络 IO 来实现,其中传输协议比较出名的有 http 、 tcp 、 udp 等等, http 、 tcp 、 udp 都是在基于 Socket 概念上为某类应用场景而扩展出的传输协议,网络 IO ,主要有 bio 、 nio 、 aio 三种方式,所有的分布式应用通讯都基于这个原理而实现,只是为了应用的易用,各种语言通常都会提供一些更为贴近应用易用的应用层协议。

    三、应用级协议Binary-RPC

    Binary-RPC(Remote Procedure Call Protocol,远程过程调用协议)是一种和RMI(Remote Method Invocation,远程方法调用)类似的远程调用的协议,它和RMI 的不同之处在于它以标准的二进制格式来定义请求的信息 ( 请求的对象、方法、参数等 ) ,这样的好处是什么呢,就是在跨语言通讯的时候也可以使用。

      Binary -RPC 协议的一次远程通信过程:

      1 、客户端发起请求,按照 Binary -RPC 协议将请求信息进行填充;

      2 、填充完毕后将二进制格式文件转化为流,通过传输协议进行传输;

      3 、接收到在接收到流后转换为二进制格式文件,按照 Binary -RPC 协议获取请求的信息并进行处理;

      4 、处理完毕后将结果按照 Binary -RPC 协议写入二进制格式文件中并返回。

    四、Hessian介绍

    Hessian是一个轻量级的remoting on http工具,采用的是Binary RPC协议,所以它很适合于发送二进制数据,同时又具有防火墙穿透能力。

    它基于HTTP协议传输,使用Hessian二进制序列化,对于数据包比较大的情况比较友好。但是它的参数和返回值都需要实现Serializable接口。

    五、示例

    1、创建服务端WebServer(Dynamic Web project)转成maven

      1)服务注解

    /**  
     * @Title:  Service.java   
     * @Package com.kamfu.annotation   
     * @Description:    TODO(用一句话描述该文件做什么)   
     * @author: liandy    
     * @date:   2019年7月26日 下午11:20:28   
     * @version V1.0 
     */
    package com.kamfu.annotation;
    
    import java.lang.annotation.ElementType;
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    import java.lang.annotation.Target;
    
    /**   
     * @ClassName:  Service   
     * @Description:服务注解类
     * @author: liandy 
     * @date:   2019年7月26日 下午11:20:28   
     *     
     */
    @Target({ElementType.TYPE})
    @Retention(RetentionPolicy.RUNTIME)
    public @interface Service {
        String name() default "";
    
    }
    Service

      2)Class辅助类

    /**  
     * @Title:  ClassUtil.java   
     * @Package kamfu.util   
     * @Description:    TODO(用一句话描述该文件做什么)   
     * @author: liandy    
     * @date:   2019年7月26日 下午10:39:29   
     * @version V1.0 
     */
    package com.kamfu.util;
    
    import java.io.File;
    import java.io.IOException;
    import java.lang.annotation.Annotation;
    import java.net.URL;
    import java.util.ArrayList;
    import java.util.List;
    
    import com.kamfu.annotation.Service;
    
    /**   
     * @ClassName:  ClassUtil   
     * @Description:Class工具类  
     * @author: liandy 
     * @date:   2019年7月27日 上午1:32:22   
     *     
     */
    public class ClassUtil {
        /**   
         * @Title: getAnnotationClassList   
         * @Description:获取指定注解的类
         * @param: @param an
         * @param: @param packageName
         * @param: @return
         * @param: @throws IOException
         * @param: @throws ClassNotFoundException      
         * @return: List<Class<?>>      
         * @throws   
         */
        public static List<Class<?>> getAnnotationClassList(String packageName,Class annotationClass) throws IOException, ClassNotFoundException
        {
            List<Class<?>> result=new ArrayList<Class<?>>();
            List<Class<?>> classes=scanPackage(packageName);
            for(Class<?> item :classes)
            {
                @SuppressWarnings("unchecked")
                Object ann=item.getAnnotation(annotationClass);
                if(ann!=null)
                {
                    result.add(item);
                }
            }
            return result;
        }
        
        /**
         * 获取同一路径下所有子类或接口实现类
         * 
         * @param intf
         * @return
         * @throws IOException
         * @throws ClassNotFoundException
         */
        public static List<Class<?>> getAllAssignedClass(Class<?> cls) throws IOException, ClassNotFoundException {
            List<Class<?>> classes = new ArrayList<Class<?>>();
            for (Class<?> c : getClasses(cls)) {
                if (cls.isAssignableFrom(c) && !cls.equals(c)) {
                    classes.add(c);
                }
            }
            return classes;
        }
        public static List<Class<?>> getAllAssignedClass(Class<?> cls,String packageName) throws IOException, ClassNotFoundException {
            List<Class<?>> classes = new ArrayList<Class<?>>();
            for (Class<?> c : scanPackage(packageName)) {
                if (cls.isAssignableFrom(c) && !cls.equals(c)) {
                    classes.add(c);
                }
            }
            return classes;
        }
        
        public static List<Class<?>> scanPackage(String packageName) throws IOException, ClassNotFoundException {
            String path = packageName.replace('.', '/');
            ClassLoader classloader = Thread.currentThread().getContextClassLoader();
            URL url = classloader.getResource(path);
            return getClasses(new File(url.getFile()), packageName);
        }
        
        /**
         * 取得当前类路径下的所有类
         * 
         * @param cls
         * @return
         * @throws IOException
         * @throws ClassNotFoundException
         */
        public static List<Class<?>> getClasses(Class<?> cls) throws IOException, ClassNotFoundException {
            String pk = cls.getPackage().getName();
            String path = pk.replace('.', '/');
            ClassLoader classloader = Thread.currentThread().getContextClassLoader();
            URL url = classloader.getResource(path);
            return getClasses(new File(url.getFile()), pk);
        }
    
        /**
         * 迭代查找类
         * 
         * @param dir
         * @param pk
         * @return
         * @throws ClassNotFoundException
         */
        private static List<Class<?>> getClasses(File dir, String pk) throws ClassNotFoundException {
            List<Class<?>> classes = new ArrayList<Class<?>>();
            if (!dir.exists()) {
                return classes;
            }
            for (File f : dir.listFiles()) {
                if (f.isDirectory()) {
                    classes.addAll(getClasses(f, pk + "." + f.getName()));
                }
                String name = f.getName();
                if (name.endsWith(".class")) {
                    classes.add(Class.forName(pk + "." + name.substring(0, name.length() - 6)));
                }
            }
            return classes;
        }
    }
    ClassUtil

      3)服务接口  

    package com.kamfu.service;
    /**  
     * @Title:  IBaseService.java   
     * @Package    
     * @Description:    TODO(用一句话描述该文件做什么)   
     * @author: liandy    
     * @date:   2019年7月26日 下午8:35:09   
     * @version V1.0 
     */
    
    /**   
     * @ClassName:  IBaseService   
     * @Description:TODO(这里用一句话描述这个类的作用)   
     * @author: liandy 
     * @date:   2019年7月26日 下午8:35:09   
     *     
     */
    public interface IBaseService {
        String test();
    }
    IBaseService

      4)服务实现类  

    /**  
     * @Title:  BaseService.java   
     * @Package com.kamfu.service   
     * @Description:    TODO(用一句话描述该文件做什么)   
     * @author: liandy    
     * @date:   2019年7月26日 下午8:36:11   
     * @version V1.0 
     */
    package com.kamfu.service;
    
    import com.kamfu.annotation.Service;
    
    /**   
     * @ClassName:  BaseService   
     * @Description:TODO(这里用一句话描述这个类的作用)   
     * @author: liandy 
     * @date:   2019年7月26日 下午8:36:11   
     *     
     */
    @Service 
    public class BaseService implements IBaseService{
    
        /**   
         * <p>Title: test</p>   
         * <p>Description: </p>   
         * @return   
         * @see com.kamfu.service.IBaseService#test()   
         */
        @Override
        public String test() {
            // TODO Auto-generated method stub
            return "{"a":"1","b":"2"}";
        }
    
    }
    BaseService

      5)自定义HessianServlet(可选)

    /**  
     * @Title:  MyHessianServlet.java   
     * @Package com.kamfu.service   
     * @Description:    TODO(用一句话描述该文件做什么)   
     * @author: liandy    
     * @date:   2019年7月26日 下午10:27:15   
     * @version V1.0 
     */
    package com.kamfu.servlet;
    
    import java.io.IOException;
    import java.io.InputStream;
    import java.io.OutputStream;
    import java.io.PrintWriter;
    import java.util.Collections;
    import java.util.HashMap;
    import java.util.Map;
    
    import javax.servlet.ServletConfig;
    import javax.servlet.ServletException;
    import javax.servlet.ServletRequest;
    import javax.servlet.ServletResponse;
    import javax.servlet.http.HttpServlet;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    
    import com.caucho.hessian.io.SerializerFactory;
    import com.caucho.hessian.server.HessianSkeleton;
    import com.caucho.services.server.ServiceContext;
    import com.kamfu.annotation.Service;
    import com.kamfu.util.ClassUtil;
    
    /**
     * @ClassName: HessianServlet
     * @Description:Servlet for serving Hessian services.
     * @author: liandy
     * @date: 2019年7月26日 下午10:27:15
     * 
     */
    @SuppressWarnings("serial")
    public class HessianServlet extends HttpServlet {
    
        private Map<String, Object> serviceImplCache = Collections.synchronizedMap(new HashMap<String, Object>());
        private Map<String, Class<?>> serviceAPICache = Collections.synchronizedMap(new HashMap<String, Class<?>>());
        private SerializerFactory _serializerFactory;
        
        public void service(ServletRequest request, ServletResponse response) throws IOException, ServletException {
    
            // logger.debug("Hessian服务调用开始");
            HttpServletRequest req = (HttpServletRequest) request;
            HttpServletResponse res = (HttpServletResponse) response;
            if (!req.getMethod().equals("POST")) {
                // res.setStatus(500); // , "Hessian Requires POST");
                PrintWriter out = res.getWriter();
    
                res.setContentType("text/html");
                out.println("<h1>Hessian Requires POST</h1>");
                out.close();
    
                return;
            }
    
            String serviceId = req.getPathInfo();
            HessianSkeleton _homeSkeleton = getHomeSkeleton(serviceId);
    
            String objectId = req.getParameter("id");
            if (objectId == null)
                objectId = req.getParameter("ejbid");
    
            ServiceContext.begin(req, res, serviceId, objectId);
    
            try {
                InputStream is = request.getInputStream();
                OutputStream os = response.getOutputStream();
    
                response.setContentType("x-application/hessian");
    
                SerializerFactory serializerFactory = getSerializerFactory();
                invoke(_homeSkeleton, is, os, objectId, serializerFactory);
    
            } catch (Throwable e) {
    
                throw new ServletException(e);
            } finally {
                ServiceContext.end();
            }
            // logger.debug("Hessian服务调用结束");
        }
        /**
         * Sets the serializer factory.
         */
        public void setSerializerFactory(SerializerFactory factory) {
            _serializerFactory = factory;
        }
    
        /**
         * Gets the serializer factory.
         */
        public SerializerFactory getSerializerFactory() {
            if (_serializerFactory == null)
                _serializerFactory = new SerializerFactory();
    
            return _serializerFactory;
        }
    
        /**
         * Sets the serializer send collection java type.
         */
        public void setSendCollectionType(boolean sendType) {
            getSerializerFactory().setSendCollectionType(sendType);
        }
    
        /**
         * Sets the debugging flag.
         */
        public void setDebug(boolean isDebug) {
        }
    
        /**
         * Sets the debugging log name.
         */
        public void setLogName(String name) {
            // _log = Logger.getLogger(name);
        }
    
        /**   
         * <p>Title: init</p>   
         * <p>Description: 初始化</p>   
         * @param config
         * @throws ServletException   
         * @see javax.servlet.GenericServlet#init(javax.servlet.ServletConfig)   
         */
        public void init(ServletConfig config) throws ServletException {
    
            super.init(config);
            try {
    
                this.registerRemoteService();
    
                if ("true".equals(getInitParameter("debug"))) {
                }
    
                if ("false".equals(getInitParameter("send-collection-type")))
                    setSendCollectionType(false);
            } catch (Throwable e) {
                // TODO PAO: 此处考虑如何处理Serverlet异常
                throw new ServletException(e);
            }
        }
    
        /**   
         * @Title: registerRemoteService   
         * @Description: 注册远端服务   
         * @param: @throws IOException
         * @param: @throws ClassNotFoundException
         * @param: @throws InstantiationException
         * @param: @throws IllegalAccessException      
         * @return: void      
         * @throws   
         */
        private void registerRemoteService()
                throws IOException, ClassNotFoundException, InstantiationException, IllegalAccessException {
            for (Class<?> c : ClassUtil.getAnnotationClassList("com.kamfu.service", Service.class)) {
                Class<?>[] interfaces = c.getInterfaces();
                if (interfaces != null && interfaces.length > 0) {
                    this.serviceImplCache.put(c.getSimpleName(), c.newInstance());
                    this.serviceAPICache.put(c.getSimpleName(), interfaces[0]);
                }
    
            }
    
        }
      /**
       * Invoke the object with the request from the input stream.
       *
       * @param in the Hessian input stream
       * @param out the Hessian output stream
       */
        protected void invoke(HessianSkeleton skeleton, InputStream is, OutputStream os, String objectId,
                SerializerFactory serializerFactory) throws Exception {
            skeleton.invoke(is, os, serializerFactory);
        }
    
    
    
        private HessianSkeleton getHomeSkeleton(String serviceId) throws ServletException {
    
            String sId = (serviceId != null && serviceId.startsWith("/")) ? serviceId.substring(1) : serviceId;
            Class<?> _homeAPI = this.getHomeAPI(sId);
    
            Object _homeImpl = this.getHomeImpl(sId);
            HessianSkeleton _homeSkeleton = new HessianSkeleton(_homeImpl, _homeAPI);
            return _homeSkeleton;
        }
    
        private Class<?> getHomeAPI(String sId) {
    
            return this.serviceAPICache.get(sId);
        }
    
        private Object getHomeImpl(String sId) {
    
            return this.serviceImplCache.get(sId);
        }
    }
    HessianServlet

      核心代码:通过服务的实例化对象及服务的接口实例化 HessianSkeleton对象。 

      

      6)maven配置

    <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
      <modelVersion>4.0.0</modelVersion>
      <groupId>WebServer</groupId>
      <artifactId>WebServer</artifactId>
      <version>0.0.1</version>
      <packaging>war</packaging>
      <build>
        <sourceDirectory>src</sourceDirectory>
        <plugins>
          <plugin>
            <artifactId>maven-compiler-plugin</artifactId>
            <version>3.8.0</version>
            <configuration>
              <source>1.6</source>
              <target>1.6</target>
            </configuration>
          </plugin>
          <plugin>
            <artifactId>maven-war-plugin</artifactId>
            <version>3.2.1</version>
            <configuration>
              <warSourceDirectory>WebContent</warSourceDirectory>
            </configuration>
          </plugin>
        </plugins>
      </build>
      <dependencies>
        <dependency>
            <groupId>hessian</groupId>
            <artifactId>hessian</artifactId>
            <version>4.0.37</version>
        </dependency>  
    <!--     <dependency> -->
    <!--         <groupId>com.kamfu.lib</groupId> -->
    <!--         <artifactId>Library</artifactId> -->
    <!--         <version>0.0.1</version> -->
    <!--     </dependency>   -->
      </dependencies>
    </project>
    pom.xml

      7)web应用配置

    <?xml version="1.0" encoding="UTF-8"?>
    <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" id="WebApp_ID" version="3.0">
      <display-name>WebServer</display-name>
        <welcome-file-list>
            <welcome-file>index.jsp</welcome-file>
        </welcome-file-list>
    
        <servlet>
            <!-- 配置 HessianServlet,Servlet的名字随便配置,例如这里配置成ServiceServlet-->
            <servlet-name>ServiceServlet</servlet-name>
            <servlet-class>com.kamfu.servlet.HessianServlet</servlet-class>
            
            <!-- 配置接口的具体实现类 -->
    <!--         <init-param> -->
    <!--             <param-name>service-class</param-name> -->
    <!--             <param-value>com.kamfu.service.BaseService</param-value> -->
    <!--         </init-param> -->
        </servlet>
        <!-- 映射 HessianServlet的访问URL地址-->
        <servlet-mapping>
            <servlet-name>ServiceServlet</servlet-name>
            <url-pattern>/*</url-pattern>
        </servlet-mapping>
    </web-app>
    web.xml

    2、创建客户端Client(Java project)转成maven

      1)客户端调用

    package com.kamfu.client;
    
    import com.caucho.hessian.client.HessianProxyFactory;
    import com.kamfu.service.IBaseService;
    
    /**
     * Hello world!
     *
     */
    public class App 
    {
        public static void main( String[] args )
        {
            try {
                String url = "http://localhost:8080/WebServer/BaseService";
                HessianProxyFactory factory = new HessianProxyFactory();
                factory.setOverloadEnabled(true);
                IBaseService basic = (IBaseService) factory.create(IBaseService.class, url);
                System.out.println(basic.test());
            }catch (Exception e){
                e.printStackTrace();
            }
    
        }
    }
    App

      2)maven配置  

    <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
      <modelVersion>4.0.0</modelVersion>
    
      <groupId>com.kamfu.client</groupId>
      <artifactId>Client</artifactId>
      <version>0.0.1-SNAPSHOT</version>
      <packaging>jar</packaging>
    
      <name>Client</name>
      <url>http://maven.apache.org</url>
    
      <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
      </properties>
    
      <dependencies>
        <dependency>
          <groupId>junit</groupId>
          <artifactId>junit</artifactId>
          <version>3.8.1</version>
          <scope>test</scope>
        </dependency>
           <dependency>
            <groupId>WebServer</groupId>
            <artifactId>WebServer</artifactId>
            <version>0.0.1</version>
        </dependency>
      </dependencies>
    </project>
    pom.xml

      3)客户端从服务端接收到的数据

      

    六、原理图

  • 相关阅读:
    pytest-multithreading实现并发运行(可以指定不参与并发的case)
    python 命令行传参方式结合jenkins构建时选择环境
    pytest结合ReportPortal使用
    loguru日志
    httprunner3 log放到allure中显示
    pytest
    python
    Mac下安装docker
    USC提出拟牛顿法深度学习优化器Apollo,效果比肩SGD和Adam
    28例电气自动控制电路图,快收藏!
  • 原文地址:https://www.cnblogs.com/liandy001/p/11253643.html
Copyright © 2020-2023  润新知