Hessian实现RPC通信

RPC即远程程序调用

RPC(Remote Procedure Call)—远程过程调用,它是一种通过网络从远程计算机程序上请求服务,而不需要了解底层网络技术的协议。RPC协议假定某些传输协议的存在,如TCP或UDP,为通信程序之间携带信息数据。在OSI网络通信模型中,RPC跨越了传输层和应用层。RPC使得开发包括网络分布式多程序在内的应用程序更加容易。

RPC采用客户机/服务器模式。请求程序就是一个客户机,而服务提供程序就是一个服务器。首先,客户机调用进程发送一个有进程参数的调用        信息到服务进程,然后等待应答信息。在服务器端,进程保持睡眠状态直到调用信息到达为止。当一个调用信息到达,服务器获得进程参数,计算结果,发送答复信息,然后等待下一个调用信息,最后,客户端调用进程接收答复信息,获得进程结果,然后调用执行继续进行。

Hessian是一个轻量级的RPC框架

它基于HTTP协议传输,使用Hessian二进制序列化,对于数据包比较大的情况比较友好。

但是它的参数和返回值都需要实现Serializable接口。

这里就不展示代码了,有个demo可供参考

说说hessian到底干了写什么吧

客户端

1.通过HessianProxyFactory生成代理对象,转发请求,等待server端回应

2.将方法以及参数都Serialized序列化,传给server端

3.因为使用的是http协议,可添加权限验证

《Hessian实现RPC通信》 流程图

首先创建HessianProxyFacotry对象,构造方法中创建了一个HessianProxyResolver对象,这个对象的lookup方法将用来查找远程服务。此外HessianProxyFacotry还有包括权限验证方面的支持。

创建了factory之后,接下来就是通过Class对象和远程服务的URL创建代理对象了。

HessianProxyFactory使用HessianProxy对象作为代理的Handler,也就是说,我们对代理对象的所有操作,都会由这个handler来处理。handler的invoke方法,在进行一些方法名和参数的确认之后,创建HttpURLConnection对象,调用sendRequest方法,将方法名和参数用HessianOutput对象(设置序列化的方式)的call方法,写入到服务端。

protected URLConnection sendRequest(String methodName, Object []args)

    throws IOException

  {

    URLConnection conn = null;

    conn = _factory.openConnection(_url);

    // Used chunked mode when available, i.e. JDK 1.5. 

  if (_factory.isChunkedPost() && conn instanceof HttpURLConnection) {

      try {

        HttpURLConnection httpConn = (HttpURLConnection) conn;

        httpConn.setChunkedStreamingMode(8 * 1024);

          } catch (Throwable e) { }

    }

    addRequestHeaders(conn);

    OutputStream os = null;

    try {

      os = conn.getOutputStream();

    } catch (Exception e) {

      throw new HessianRuntimeException(e);

    }

    try {

      if (log.isLoggable(Level.FINEST)) {

    PrintWriter dbg = new PrintWriter(new LogWriter(log));

    os = new HessianDebugOutputStream(os, dbg);

      }

      AbstractHessianOutput out = _factory.getHessianOutput(os);

      out.call(methodName, args);

      out.flush();

      return conn;

    } catch (IOException e) {

      if (conn instanceof HttpURLConnection)

    ((HttpURLConnection) conn).disconnect();

      throw e;

    } catch (RuntimeException e) {

      if (conn instanceof HttpURLConnection)

    ((HttpURLConnection) conn).disconnect();

      throw e;

    }

  }

服务端拿到请求,进行反序列化,然后将方法调用,再将结果序列化之后写回到connection。所以,客户端在sendRequest之后,所要做的就是将返回的结果进行解析,看返回的code是不是200:

conn = sendRequest(mangleName, args);

      if (conn instanceof HttpURLConnection) {

    httpConn = (HttpURLConnection) conn;

        int code = 500;

        try {

          code = httpConn.getResponseCode();

        } catch (Exception e) {

        }

        parseResponseHeaders(conn);

        if (code != 200) {

          StringBuffer sb = new StringBuffer();

          int ch;

…..

        AbstractHessianInput in = _factory.getHessianInput(is);

      in.startReply();

      Object value = in.readObject(method.getReturnType());

      if (value instanceof InputStream) {

    value = new ResultInputStream(httpConn, is, in, (InputStream) value);

    is = null;

    httpConn = null;

      }

      else    in.completeReply();

      return value;

解析HessianInput对象,并且从中读取到结果返回。

服务器端

1.初始化的时候生成一个HessianServle,在init()的时候生成methodMap;

2.service()方法会调用 _objectSkeleton.invoke()实现方法的运行

3.写会到客户端

《Hessian实现RPC通信》 流程图

我们所有的工作都围绕在HessianServlet在展开。该Servlet中有两个比较重要的方法:init()、service();

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

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

if (_homeImpl != null) { }

 else if (getInitParameter(“home-class”) != null) {

        String className = getInitParameter(“home-class”);

        Class homeClass = loadClass(className);

        _homeImpl = homeClass.newInstance();

        init(_homeImpl);

      }

else if (getInitParameter(“service-class”) != null) {

        String className = getInitParameter(“service-class”);

        Class homeClass = loadClass(className);

        _homeImpl = homeClass.newInstance();

        init(_homeImpl);

      }

else {

    if (getClass().equals(HessianServlet.class))

      throw new ServletException(“server must extend HessianServlet”);

    _homeImpl = this;

      }

通过home-api或者api-class加载实现类的接口对象;

if (_homeAPI != null) {  }

else if (getInitParameter(“home-api”) != null) {

        String className = getInitParameter(“home-api”);

        _homeAPI = loadClass(className);

      }

else if (getInitParameter(“api-class”) != null) {

        String className = getInitParameter(“api-class”);

        _homeAPI = loadClass(className);

      }

else if (_homeImpl != null) _homeAPI = _homeImpl.getClass();

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

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

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

public void service(ServletRequest request, ServletResponse response)

    throws IOException, ServletException

  {

    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(“Hessian Requires POST”);

      return;

    }

    String serviceId = req.getPathInfo();

    String objectId = req.getParameter(“id”);

    if (objectId == null)

      objectId = req.getParameter(“ejbid”);

    ServiceContext.begin(req, serviceId, objectId);

    try {

      InputStream is = request.getInputStream();

      OutputStream os = response.getOutputStream();

      HessianInput in = new HessianInput(is);

      HessianOutput out = new HessianOutput(os);

      if (objectId != null)

    _objectSkeleton.invoke(in, out);

      else    _homeSkeleton.invoke(in, out);

    } catch (RuntimeException e) {

      throw e;

    } catch (ServletException e) {

      throw e;

    } catch (Throwable e) {

      throw new ServletException(e);

    } finally {

      ServiceContext.end();

    }

  }

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

invoke方法:

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

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

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

Spring也为Hessian提供了很友好的支持,通过使用spring-remoting包,我们可以很方便地发布和调用服务。

最原始的实现,我们的服务是通过Servlet来绑定的,而Spring的实现,我们使用了SpringMVC的加载时机,将配置文件加载。HessianServiceExporter

public classHessianServiceExporterextendsRemoteExporterimplementsHttpRequestHandler,InitializingBean{….}

这个类实现了InitializingBean接口,这是spring-beans包中很重要的一个扩展接口。

这个接口的说明如下:

Interface to be implemented by beans that need to react once all their properties have been set by a BeanFactory: for example, to perform custom initialization, or merely to check that all mandatory properties have been set.

也就是说,它会随着Spring容器(此处为Spring MVC容器)的启动而被加载。看看HessianServiceExporter的实现:

public void prepare() {

        HessianSkeleton skeleton = null;

        try {

            try {

                Constructor ctor = (class$com$caucho$hessian$server$HessianSkeleton == null?(class$com$caucho$hessian$server$HessianSkeleton = class$(“com.caucho.hessian.server.HessianSkeleton”)) : class$com$caucho$hessian$server$HessianSkeleton).getConstructor(new Class[]{class$java$lang$Object == null?(class$java$lang$Object = class$(“java.lang.Object”)):class$java$lang$Object, class$java$lang$Class == null?(class$java$lang$Class = class$(“java.lang.Class”)):class$java$lang$Class});

                this.checkService();

                this.checkServiceInterface();

                skeleton = (HessianSkeleton)ctor.newInstance(new Object[]{this.getProxyForService(), this.getServiceInterface()});

            } catch (NoSuchMethodException var4) {

                Constructor ctor = (class$com$caucho$hessian$server$HessianSkeleton == null?(class$com$caucho$hessian$server$HessianSkeleton = class$(“com.caucho.hessian.server.HessianSkeleton”)):class$com$caucho$hessian$server$HessianSkeleton).getConstructor(new Class[]{class$java$lang$Object == null?(class$java$lang$Object = class$(“java.lang.Object”)):class$java$lang$Object});

                skeleton = (HessianSkeleton)ctor.newInstance(new Object[]{this.getProxyForService()});

            }

        } catch (Throwable var5) {

            throw new BeanInitializationException(“Hessian skeleton initialization failed”, var5);

        }

        if(hessian2Available) {

            this.skeletonInvoker = new Hessian2SkeletonInvoker(skeleton, this.serializerFactory);

        } else {

            this.skeletonInvoker = new Hessian1SkeletonInvoker(skeleton, this.serializerFactory);

        }

    }

    public void handleRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

        Assert.notNull(this.skeletonInvoker, “HessianServiceExporter has not been initialized”);

        if(!”POST”.equals(request.getMethod())) {

            throw new HttpRequestMethodNotSupportedException(“POST”, “HessianServiceExporter only supports POST requests”);

        } else {

            try {

                this.skeletonInvoker.invoke(request.getInputStream(), response.getOutputStream());

            } catch (Throwable var4) {

                throw new NestedServletException(“Hessian skeleton invocation failed”, var4);

            }

        }

    }

在prepare方法中,获取service和serviceInterface的配置,创建HessianSkeleton对象。 

同时,还实现了HttpRequestHandler,spring-web中的接口。 

又因为实现了HttpRequestHandler接口,所以在handleRequest方法中,可以像HessianServlet的service方法一样,调用Hessian2SkeletonInvoker的invoke方法进行实际的方法调用。

spring boot 中Hessian的配置就不多言,Demo就是一个小例子

    原文作者:谁在烽烟彼岸
    原文地址: https://www.jianshu.com/p/07a371a68961
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞