`
leixbo
  • 浏览: 32709 次
  • 性别: Icon_minigender_1
  • 来自: 深圳
社区版块
存档分类
最新评论

Hessian之服务端原理

    博客分类:
  • Java
阅读更多

 

一、简介 

 

     Hessian是一个由Caucho Technology开发的轻量级二进制RPC工具,与普通的RPC实现方式不同的是,它是基于 Http 协议进行的数据传输。

     Hessian通常通过Web应用来提供服务,非常类似于WebService,但它不使用SOAP协议。相比WebService,Hessian更简单、快捷、轻量级。

 

     与一般的RPC实现方式一样,它的处理过程如下: 

         客户端 -> 序列化写到输出流 -> 远程方法(服务器端)-> 序列化写到输出流  -> 客户端读取输入流 -> 输出结果

 

二、代码分析

 

      hessian的服务端就是一个servlet,拦截请求,然后分析解析参数,调用对应的local服务。入口类为com.caucho.hessian.server.HessianServlet,该类基础自HttpServlet,就是一个普通的servlet,配置时将某个路径的请求映射到这个Servlet上(与Struts1的org.apache.struts.action.ActionServlet类似吧),既然是一个servlet,那我们看看service方法都做了些什么。

 

/**
   * Execute a request.  The path-info of the request selects the bean.
   * Once the bean's selected, it will be applied.
   */
  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("<h1>Hessian Requires POST</h1>");
      
      return;
    }

    String serviceId = req.getPathInfo();
    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(is, os, objectId, serializerFactory);
    } catch (...Exception e) {
      //....
      throw new ServletException(e);
    } finally {
      ServiceContext.end();
    }
  }

 

 1、请求方式的验证

 

       我们可以看到该方法对请求方法进行了验证,非POST的请求方式将不进行处理(通过hessian客户端方式调用的为POST方式,二进制流都应该是post方式),可以在浏览器上测试,输入请求地址,应该会返回500错误码。

    

 2、上下文数据保存

 

      ServiceContext.begin(req, res, serviceId, objectId)进行了上下数据的保存

 

public static void begin(ServletRequest request,
                           ServletResponse response,
                           String serviceName,
                           String objectId)
    throws ServletException
  {
    ServiceContext context = (ServiceContext) _localContext.get();

    if (context == null) {
      context = new ServiceContext();
      _localContext.set(context);
    }

    context._request = request;
    context._response = response;
    context._serviceName = serviceName;
    context._objectId = objectId;
    context._count++;
  }

  

      _localContext是一个ThreadLocal对象,ServiceContext用来保存当前调用线程上下文的一些信息,如request、response等非常重要的数据

 

3、将输入输出流,序列化工厂传入invoke方法,进行调用invoke(is, os, objectId, serializerFactory)

 

这个方法内部实际上是调用了HessianSkeleton对象的invoke方法

 

protected void invoke(InputStream is, OutputStream os,
                        String objectId,
                        SerializerFactory serializerFactory)
    throws Exception
  {
    if (objectId != null)
      _objectSkeleton.invoke(is, os, serializerFactory);
    else
      _homeSkeleton.invoke(is, os, serializerFactory);
  }

 

 _objectSkeleton与_homeSkeleton都为HessianSkeleton对象,暂时没看懂这两个对象有什么的区别

 

4、看看HessianSkeleton 的invoke(InputStream, OutputStream,SerializerFactory)方法做了什么

 

/**
   * Invoke the object with the request from the input stream.
   *
   * @param in the Hessian input stream
   * @param out the Hessian output stream
   */
  public void invoke(InputStream is, OutputStream os,
                     SerializerFactory serializerFactory)
    throws Exception
  {
    //......

    HessianInputFactory.HeaderType header = _inputFactory.readHeader(is);

    AbstractHessianInput in;
    AbstractHessianOutput out;

    switch (header) {
    case CALL_1_REPLY_1:
      in = _hessianFactory.createHessianInput(is);
      out = _hessianFactory.createHessianOutput(os);
      break;

    case CALL_1_REPLY_2:
      in = _hessianFactory.createHessianInput(is);
      out = _hessianFactory.createHessian2Output(os);
      break;

    case HESSIAN_2:
      in = _hessianFactory.createHessian2Input(is);
      in.readCall();
      out = _hessianFactory.createHessian2Output(os);
      break;

    default:
      throw new IllegalStateException(header + " is an unknown Hessian call");
    }

    //....
      invoke(_service, in, out);
    //....
  }

 

       invoke方法先解析请求头,因为hessian有不同的版本,并且分请求与相应的请求头,因此有多种hederType:( CALL_1_REPLY_1、CALL_1_REPLY_2、HESSIAN_2、REPLY_1、 REPLY_2)

然后根据不同的请求类型,构建不同的AbstractHessianInput、AbstractHessianOutput对象(不同的hederType,处理有所差别),然后最终也是调用invoke重载方法,把AbstractHessianInput、AbstractHessianOutput对象作为参数传递进去

 

 

  

 5、看看真正做事的invoke方法

 

 /**
   * Invoke the object with the request from the input stream.
   *
   * @param in the Hessian input stream
   * @param out the Hessian output stream
   */
  public void invoke(Object service,
                     AbstractHessianInput in,
                     AbstractHessianOutput out)
    throws Exception
  {
    
    //..

    Object []values = new Object[args.length];

    for (int i = 0; i < args.length; i++) {
      // XXX: needs Marshal object
      values[i] = in.readObject(args[i]);
    }

    Object result = null;

    try {
      result = method.invoke(service, values);
    } catch (Exception e) {
      //..
    }
    //..
  }

 

       可以看到通过AbstractHessianInput对象,进行了参数的获取,包括请求的方法,请求的参数,然后利用反射调用调用service的方法,最后将处理结果写回去

 

       到此整个流程就完了,与一般的RPC处理方式类似吧,只是在参数的传递、解析时有所不同,但最终所达到的目的都是一样的

 

 

 

 

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics