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)客户端从服务端接收到的数据

  

 

六、原理图

 

转载于:https://www.cnblogs.com/liandy001/p/11253643.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值