hessian源码分析

TiDBPingCAP 2011-10-12

1.hessian源码分析(一)------架构

hessian的主要结构分客户端与服务端,中间基于http传输。客户端主要做的事情是把对远程接口调用序列化为流,并传输到服务端;服务端主要做的事情是把传输过来的流反序列化为对服务的请求,调用相应服务后把结果序列化为流返回给客户端。一次完整的调用如下图所示:

hessian源码分析

HessianProxy是hessian client处理客户端请求的核心类,它采用proxy的设计模式,代理客户端对远程接口的调用,hessian client的主流程的时序图如下所示:

hessian源码分析 

HessianSkeleton是hessian server端的核心类,从输入流中返序列化出客户端调用的方法和参数,对服务端服务进行调用,然后把处理结果返回给客户端,主要流程时序图如下所示:

 hessian源码分析

2.Hessian源码分析(二)------ HessianProxy

Hessian在客户端一块采用Proxy模式,当客户端调用远程接口时,HessianProxy会代理这个动作,在invoke方法中,把客户端请求的方法和参数序列化为预订格式的输出流,主要流程如下图所示:

hessian源码分析

下面我将详细解析一下invoke源码:

public Object invoke(Object proxy, Method method, Object []args)  
    throws Throwable  
  {  
    String mangleName;  
    synchronized (_mangleMap) {  
      mangleName = _mangleMap.get(method);  
    }  
    if (mangleName == null) {  
      String methodName = method.getName();  
      Class []params = method.getParameterTypes();  
      // equals and hashCode are special cased  
      if (methodName.equals("equals")  
      && params.length == 1 && params[0].equals(Object.class)) {  
    Object value = args[0];  
    if (value == null || ! Proxy.isProxyClass(value.getClass()))  
      return new Boolean(false);  
    HessianProxy handler = (HessianProxy) Proxy.getInvocationHandler(value);  
    return new Boolean(_url.equals(handler.getURL()));  
      }  
      else if (methodName.equals("hashCode") && params.length == 0)  
    return new Integer(_url.hashCode());  
      else if (methodName.equals("getHessianType"))  
    return proxy.getClass().getInterfaces()[0].getName();  
      else if (methodName.equals("getHessianURL"))  
    return _url.toString();  
      else if (methodName.equals("toString") && params.length == 0)  
    return "HessianProxy[" + _url + "]";  
        
      if (! _factory.isOverloadEnabled())  
    mangleName = method.getName();  
      else  
        mangleName = mangleName(method);  
      synchronized (_mangleMap) {  
    _mangleMap.put(method, mangleName);  
      }  
    }  
    ......  
}  

这是invoke的开头,主要干的事情是把methodName缓存起来和过滤一些特殊调用,java反射是个比较耗性能的操作,把methodName缓存起来可以避免每次调用都要从method里得到methodName。另外,对equals、hashCode、getHessianType、getHessianURL等特殊方法的远程调用,HessianProxy不会走远程调用,而是直接返回。

接着往下看:

public Object invoke(Object proxy, Method method, Object []args)  
    throws Throwable  
  {  
    ......  
    InputStream is = null;  
    URLConnection conn = null;  
    HttpURLConnection httpConn = null;  
      
    try {  
             
      conn = sendRequest(mangleName, args);  
     ......  
    } catch (HessianProtocolException e) {  
      throw new HessianRuntimeException(e);  
    } finally {  
     ......  
     }  
  }  

这段主要是把方法名和参数序列化为网络输出流,并做网络请求,具体逻辑包装在sendRequest方法中:

protected URLConnection sendRequest(String methodName, Object []args)  
    throws IOException  
  {  
    URLConnection conn = null;  
      
    conn = _factory.openConnection(_url);  
    boolean isValid = false;  
    try {  
      // 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);  
      }  
      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();  
      isValid = true;  
      return conn;  
    } finally {  
      if (! isValid && conn instanceof HttpURLConnection)  
    ((HttpURLConnection) conn).disconnect();  
    }  
  }  

sendRequest的主要流程是先初始化网络连接,然后用AbstractHessianOutput包装网络输出流,调用AbstractHessianOutput.call(methodName, args)完成网络输出,这个方法的细节会在hessian io的源码解析中详细分析。

下面接着看invoke方法:

public Object invoke(Object proxy, Method method, Object []args)  
    throws Throwable  
  {  
      ......  
      is = conn.getInputStream();  
      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();  
      ......  
  }  

这一段主要是把输入流中取得返回值,具体是用AbstractHessianInput包装网络输入流,然后调用AbstractHessianInput.readObject从网络流中反序列化到实际返回值。

3.Hessian源码分析(三)------ HessianSkeleton

HessianSkeleton是Hessian server端的核心类,主要功能是接收网络输入流(被包装为AbstractHessianInput),反序列化输入流得到methodName和参数,然后调用服务端的服务,得到结果后序列化为输出流,返回给客户端,主要流程如下图所示:

hessian源码分析

HessianSkeleton的核心代码如下所示:

public void invoke(Object service,  
             AbstractHessianInput in,  
             AbstractHessianOutput out)  
    throws Exception  
  {  
    ......      
    String methodName = in.readMethod();  
    Method method = getMethod(methodName);  
    ......  
    Class []args = method.getParameterTypes();  
    Object []values = new Object[args.length];  
    for (int i = 0; i < args.length; i++) {  
      values[i] = in.readObject(args[i]);  
    }  
    Object result = null;  
      
    try {  
      result = method.invoke(service, values);  
    } catch (Throwable e) {  
      ......  
    }  
    // The complete call needs to be after the invoke to handle a  
    // trailing InputStream  
    in.completeCall();  
      
    out.startReply();  
    out.writeObject(result);  
      
    out.completeReply();  
    out.close();  
  }  

主流程代码非常清晰,不需要太多解释,关键的地方在于对网络流的序列化和反序列化,我会在hessian io分析的部分中进行详细阐述

原文出自:http://blog.csdn.net/cutesource/article/details/4946968

相关推荐

TiDBPingCAP / 0评论 2020-07-29