JAVA RMI线程模型及内部实现机制


JAVA RMI线程模型及内部实现机制
2010年06月07日
  JAVA RMI是JAVA分布式结构的基础。远程对象的通信过程中,RMI使用标准机制:stub和skeleton。远程对象的stub担当远程对象的客户本地代表或代理人角色,调用程序将调用本地stub的方法,而本地stub将负责执行对远程对象的方法调用。在RMI中,远程对象的stub与该远程对象所实现的远程接口集相同。调用stub的方法时将执行下列操作:
  (1) 初始化与包含远程对象的远程虚拟机的连接;
  (2) 对远程虚拟机的参数进行编组-传输参数;
  (3) 等待远程方法调用结果;
  (4) 解编(读取)返回值或返回的异常;
  (5) 将值返回给调用程序。
  为了向调用程序展示比较简单的调用机制,stub将参数的序列化和网络级通信等细节隐藏了起来。在远程虚拟机中,每个远程对象都可以有相应的skeleton。skeleton负责将调用分配给实际的远程对象实现。它在接收方法调用时执行下列操作:
  (1) 解编(读取)远程方法的参数;
  (2) 调用实际远程对象实现上的方法;
  (3) 将结果(返回值或异常)编组(写入并传输)给调用程序。  
  stub和skeleton由rmic编译器生成。在最新的JDK中,不需要手工生产stub和skeleton,用动态代理生成的Proxy代替了stub,而skeleton则取消了。
  我们可以查看源代码来了解RMI的内部实现。Server端调用UnicastRemoteObject的export方法输出远程对象,export方法会在一个线程里监听某个TCP端口上的方法调用请求:  public void exportObject(Target target) throws RemoteException { // other code while (true) { ServerSocket myServer = server; if (myServer == null) return; Throwable acceptFailure = null; final Socket socket; try { socket = myServer.accept(); /* * Find client host name (or "0.0.0.0" if unknown) */ InetAddress clientAddr = socket.getInetAddress(); String clientHost = (clientAddr != null ? clientAddr.getHostAddress() : "0.0.0.0"); /* * Spawn non-system thread to handle the connection */ Thread t = (Thread) java.security.AccessController.doPrivileged ( new NewThreadAction(new ConnectionHandler(socket, clientHost), "TCP Connection(" + ++ threadNum + ")-" + clientHost, true, true)); t.start(); } catch (IOException e) { acceptFailure = e; } catch (RuntimeException e) { acceptFailure = e; } catch (Error e) { acceptFailure = e; } } // other code }   上面的代码已被修改以展示主要的要点,Server端就是在ServerSocket的accept方法上面监听到来的请求,如果有新的方法调用请求到来,Server产生一个单独的线程来处理新接收的请求:  public void dispatch(Remote obj, RemoteCall call) throws IOException { // positive operation number in 1.1 stubs; // negative version number in 1.2 stubs and beyond... int num; long op; try { // read remote call header ObjectInput in; try { in = call.getInputStream(); num = in.readInt(); if (num >= 0) { if (skel != null) { oldDispatch(obj, call, num); return; } else { throw new UnmarshalException( "skeleton class not found but required " + "for client version"); } } op = in.readLong(); } catch (Exception readEx) { throw new UnmarshalException("error unmarshalling call header", readEx); } /* * Since only system classes (with null class loaders) will be on * the execution stack during parameter unmarshalling for the 1.2 * stub protocol, tell the MarshalInputStream not to bother trying * to resolve classes using its superclasses's default method of * consulting the first non-null class loader on the stack. */ MarshalInputStream marshalStream = (MarshalInputStream) in; marshalStream.skipDefaultResolveClass(); Method method = (Method) hashToMethod_Map.get(new Long(op)); if (method == null) { throw new UnmarshalException("invalid method hash"); } // if calls are being logged, write out object id and operation logCall(obj, method); // unmarshal parameters Class[] types = method.getParameterTypes(); Object[] params = new Object[types.length]; try { unmarshalCustomCallData(in); for (int i = 0; i 结果写入到输出流中:marshalValue(rtype, result, out)。一个方法调用就执行完成了。
  Client端请求一个远程方法调用时,先建立连接:Connection conn = ref.getChannel().newConnection(),然后发送方法参数: marshalValue(types[i], params[i], out),再发送执行方法请求:call.executeCall(),最后得到方法的执行结果:Object returnValue = unmarshalValue(rtype, in),并关闭接:
  ref.getChannel().free(conn, true)。  public Object invoke(Remote obj, java.lang.reflect.Method method, Object[] params, long opnum) throws Exception { if (clientRefLog.isLoggable(Log.VERBOSE)) { clientRefLog.log(Log.VERBOSE, "method: " + method); } if (clientCallLog.isLoggable(Log.VERBOSE)) { logClientCall(obj, method); } Connection conn = ref.getChannel().newConnection(); RemoteCall call = null; boolean reuse = true; /* If the call connection is "reused" early, remember not to * reuse again. */ boolean alreadyFreed = false; try { if (clientRefLog.isLoggable(Log.VERBOSE)) { clientRefLog.log(Log.VERBOSE, "opnum = " + opnum); } // create call context call = new StreamRemoteCall(conn, ref.getObjID(), -1, opnum); // marshal parameters try { ObjectOutput out = call.getOutputStream(); marshalCustomCallData(out); Class[] types = method.getParameterTypes(); for (int i = 0; i socket, clientHost), "TCP Connection(" + ++ threadNum + ")-" + clientHost, true, true));   在JDK1.6之后,RMI使用线程池来处理新接收的远程方法调用请求-ThreadPoolExecutor。
  下面是一个简单的RMI程序的执行线程抓图,我们可以更好的了解RMI的线程机制。这个简单的RMI程序是服务端有一个远程方法实现,一个客户端同时请求执行这个远程方法100次。在JDK1.5中执行时生成的线程如下图所示,每个方法调用请求都是在一个单独的线程里执行,即 A Thread per Request。
  
  在JDK1.6中执行时生成的线程如下图所示,这些线程都是在ThreadPoolExecutor线程池中执行的。
  
  在JDK1.6中,RMI提供了可配置的线程池参数属性:
  sun.rmi.transport.tcp.maxConnectionThread - 线程池中的最大线程数量
  sun.rmi.transport.tcp.threadKeepAliveTime - 线程池中空闲的线程存活时间

猜你喜欢

转载自kleu10kleu.iteye.com/blog/1363398