hessian的主要结构分客户端与服务端,中间基于http传输。客户端主要做的事情是把对远程接口调用序列化为流,并传输到服务端;服务端主要做的事情是把传输过来的流反序列化为对服务的请求,调用相应服务后把结果序列化为流返回给客户端。一次完整的调用如下图所示:
HessianProxy是hessian client处理客户端请求的核心类,它采用proxy的设计模式,代理客户端对远程接口的调用,hessian client的主流程的时序图如下所示:
HessianSkeleton是hessian server端的核心类,从输入流中返序列化出客户端调用的方法和参数,对服务端服务进行调用,然后把处理结果返回给客户端,主要流程时序图如下所示:
Hessian在客户端一块采用Proxy模式,当客户端调用远程接口时,HessianProxy会代理这个动作,在invoke方法中,把客户端请求的方法和参数序列化为预订格式的输出流,主要流程如下图所示:
下面我将详细解析一下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从网络流中反序列化到实际返回值。
HessianSkeleton是Hessian server端的核心类,主要功能是接收网络输入流(被包装为AbstractHessianInput),反序列化输入流得到methodName和参数,然后调用服务端的服务,得到结果后序列化为输出流,返回给客户端,主要流程如下图所示:
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分析的部分中进行详细阐述