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

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

1 RMI内部实现

  JAVA RMI是JAVA分布式结构的基础。 远程对象的通信过程中, RMI 使用标准机制: stub 和skeleton 。远程对象的 stub 担当远程对象的客户本地代表或代理人角色,调用程序将调 用本地 stub的方法,而本地 stub 将负责执行对远程对象的方法调用。在 RMI 中,远程对象的 stub 与该远程对象所实现的远程接口集相同。调用 stub 的方法时将执行下列操作: 
   (1) 初始化与包含远程对象的远程虚拟机的连接; 
  (2) 对远程虚拟机的参数进行编组-传输参数; 
  (3) 等待远程方法调用结果; 
  (4) 解编(读取)返回值或返回的异常; 
  (5) 将值返回给调用程序。

  为了向调用程序展示比较简单的调用机制, stub 将参数的序列化和网络级通信等细节隐藏了起来。在远程虚拟机中,每个远程对象都可以 有相应的 skeleton。s keleton 负责将调用分配给实际的远程对象实现。它在接收方法调用时 执行下列操作:

  (1) 解编(读取)远程方法的参数;

  (2) 调用实际远程对象实现上的方法;

  (3) 将结果(返回值或异常)编组(写入并传输)给调用程序。  

  stub 和 skeleton 由 rmic 编译器生成。在最新的JDK中,不需要手工生产stub和skeleton,用动态代理生成的Proxy代替了stub,而skeleton则取消了。

  我们可以查看源代码来了解RMI的内部实现。Server端调用UnicastRemoteObject的export方法输出远程对象,export方法会在一个线程里监听某个TCP端口上的方法调用请求:

[java]  view plain copy
  1. public void exportObject(Target target) throws RemoteException {  
  2.     
  3.   // other code  
  4.     
  5. while (true) {  
  6.     ServerSocket myServer = server;  
  7.     if (myServer == null)  
  8.       return;  
  9.     Throwable acceptFailure = null;  
  10.     final Socket socket;  
  11.       
  12.     try {  
  13.     socket = myServer.accept();  
  14.       
  15.     /* 
  16.      * Find client host name (or "0.0.0.0" if unknown) 
  17.      */  
  18.     InetAddress clientAddr = socket.getInetAddress();  
  19.     String clientHost = (clientAddr != null  
  20.                  ? clientAddr.getHostAddress()  
  21.                  : "0.0.0.0");  
  22.       
  23.     /* 
  24.      * Spawn non-system thread to handle the connection 
  25.      */  
  26.     Thread t = (Thread)  
  27.         java.security.AccessController.doPrivileged (  
  28.             new NewThreadAction(new ConnectionHandler(socket,  
  29.                               clientHost),  
  30.                     "TCP Connection(" + ++ threadNum +  
  31.                     ")-" + clientHost,  
  32.                     truetrue));  
  33.     t.start();  
  34.       
  35.     } catch (IOException e) {  
  36.     acceptFailure = e;  
  37.     } catch (RuntimeException e) {  
  38.     acceptFailure = e;  
  39.     } catch (Error e) {  
  40.     acceptFailure = e;  
  41.     }  
  42. }  
  43.   
  44. // other code  

  上面的代码已被修改以展示主要的要点,Server端就是在ServerSocket的accept方法上面监听到来的请求,如果有新的方法调用请求到来,Server产生一个单独的线程来处理新接收的请求:

[java]  view plain copy
  1. public void dispatch(Remote obj, RemoteCall call) throws IOException {  
  2. // positive operation number in 1.1 stubs;  
  3. // negative version number in 1.2 stubs and beyond...  
  4. int num;  
  5. long op;  
  6. try {  
  7.     // read remote call header  
  8.     ObjectInput in;  
  9.     try {  
  10.     in = call.getInputStream();  
  11.     num = in.readInt();  
  12.     if (num >= 0) {  
  13.         if (skel != null) {  
  14.         oldDispatch(obj, call, num);  
  15.         return;  
  16.         } else {  
  17.         throw new UnmarshalException(  
  18.             "skeleton class not found but required " +  
  19.             "for client version");  
  20.         }  
  21.     }  
  22.     op = in.readLong();  
  23.     } catch (Exception readEx) {  
  24.     throw new UnmarshalException("error unmarshalling call header",  
  25.                      readEx);  
  26.     }  
  27.     /* 
  28.      * Since only system classes (with null class loaders) will be on 
  29.      * the execution stack during parameter unmarshalling for the 1.2 
  30.      * stub protocol, tell the MarshalInputStream not to bother trying 
  31.      * to resolve classes using its superclasses's default method of 
  32.      * consulting the first non-null class loader on the stack. 
  33.      */  
  34.     MarshalInputStream marshalStream = (MarshalInputStream) in;  
  35.     marshalStream.skipDefaultResolveClass();  
  36.     Method method = (Method) hashToMethod_Map.get(new Long(op));  
  37.     if (method == null) {  
  38.     throw new UnmarshalException("invalid method hash");  
  39.     }  
  40.     // if calls are being logged, write out object id and operation  
  41.     logCall(obj, method);  
  42.     // unmarshal parameters  
  43.     Class[] types = method.getParameterTypes();  
  44.     Object[] params = new Object[types.length];  
  45.     try {  
  46.     unmarshalCustomCallData(in);  
  47.     for (int i = 0; i < types.length; i++) {  
  48.         params[i] = unmarshalValue(types[i], in);  
  49.     }  
  50.     } catch (java.io.IOException e) {  
  51.     throw new UnmarshalException(  
  52.         "error unmarshalling arguments", e);  
  53.     } catch (ClassNotFoundException e) {  
  54.     throw new UnmarshalException(  
  55.         "error unmarshalling arguments", e);  
  56.     } finally {  
  57.     call.releaseInputStream();  
  58.     }  
  59.     // make upcall on remote object  
  60.     Object result;  
  61.     try {  
  62.     result = method.invoke(obj, params);  
  63.     } catch (InvocationTargetException e) {  
  64.     throw e.getTargetException();  
  65.     }  
  66.     // marshal return value  
  67.     try {  
  68.     ObjectOutput out = call.getResultStream(true);  
  69.     Class rtype = method.getReturnType();  
  70.     if (rtype != void.class) {  
  71.         marshalValue(rtype, result, out);  
  72.     }  
  73.     } catch (IOException ex) {  
  74.     throw new MarshalException("error marshalling return", ex);  
  75.     /* 
  76.      * This throw is problematic because when it is caught below, 
  77.      * we attempt to marshal it back to the client, but at this 
  78.      * point, a "normal return" has already been indicated, 
  79.      * so marshalling an exception will corrupt the stream. 
  80.      * This was the case with skeletons as well; there is no 
  81.      * immediately obvious solution without a protocol change. 
  82.      */  
  83.     }  
  84. catch (Throwable e) {  
  85.     logCallException(e);  
  86.       
  87.     ObjectOutput out = call.getResultStream(false);  
  88.     if (e instanceof Error) {  
  89.     e = new ServerError(  
  90.         "Error occurred in server thread", (Error) e);  
  91.     } else if (e instanceof RemoteException) {  
  92.     e = new ServerException(  
  93.         "RemoteException occurred in server thread",  
  94.         (Exception) e);  
  95.     }  
  96.     if (suppressStackTraces) {  
  97.     clearStackTraces(e);  
  98.     }  
  99.     out.writeObject(e);  
  100. finally {  
  101.     call.releaseInputStream(); // in case skeleton doesn't  
  102.     call.releaseOutputStream();  
  103. }  
  104.    }  
  105. protected static void marshalValue(Class type, Object value,  
  106.                    ObjectOutput out)  
  107. throws IOException  
  108.    {  
  109. if (type.isPrimitive()) {  
  110.     if (type == int.class) {  
  111.     out.writeInt(((Integer) value).intValue());  
  112.     } else if (type == boolean.class) {  
  113.     out.writeBoolean(((Boolean) value).booleanValue());  
  114.     } else if (type == byte.class) {  
  115.     out.writeByte(((Byte) value).byteValue());  
  116.     } else if (type == char.class) {  
  117.     out.writeChar(((Character) value).charValue());  
  118.     } else if (type == short.class) {  
  119.     out.writeShort(((Short) value).shortValue());  
  120.     } else if (type == long.class) {  
  121.     out.writeLong(((Long) value).longValue());  
  122.     } else if (type == float.class) {  
  123.     out.writeFloat(((Float) value).floatValue());  
  124.     } else if (type == double.class) {  
  125.     out.writeDouble(((Double) value).doubleValue());  
  126.     } else {  
  127.     throw new Error("Unrecognized primitive type: " + type);  
  128.     }  
  129. else {  
  130.     out.writeObject(value);  
  131. }  
  132.    }  
  133. rotected static Object unmarshalValue(Class type, ObjectInput in)  
  134. throws IOException, ClassNotFoundException  
  135.    {  
  136. if (type.isPrimitive()) {  
  137.     if (type == int.class) {  
  138.     return new Integer(in.readInt());  
  139.     } else if (type == boolean.class) {  
  140.     return new Boolean(in.readBoolean());  
  141.     } else if (type == byte.class) {  
  142.     return new Byte(in.readByte());  
  143.     } else if (type == char.class) {  
  144.     return new Character(in.readChar());  
  145.     } else if (type == short.class) {  
  146.     return new Short(in.readShort());  
  147.     } else if (type == long.class) {  
  148.     return new Long(in.readLong());  
  149.     } else if (type == float.class) {  
  150.     return new Float(in.readFloat());  
  151.     } else if (type == double.class) {  
  152.     return new Double(in.readDouble());  
  153.     } else {  
  154.     throw new Error("Unrecognized primitive type: " + type);  
  155.     }  
  156. else {  
  157.     return in.readObject();  
  158. }  
  159.    }  

   dispatch方法处理接收的请求,先从输入流中读取方法的编号来获得方法名称:op = in.readLong(),然后读取方法的所有参数:params[i] = unmarshalValue(types[i], in),接着就可以执行方法调用了:result = method.invoke(obj, params),最后把方法执行结果写入到输出流中: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)。

[java]  view plain copy
  1.  public Object invoke(Remote obj,  
  2.          java.lang.reflect.Method method,  
  3.          Object[] params,  
  4.          long opnum)  
  5. throws Exception  
  6.    {  
  7. if (clientRefLog.isLoggable(Log.VERBOSE)) {  
  8.     clientRefLog.log(Log.VERBOSE, "method: " + method);  
  9. }  
  10. if (clientCallLog.isLoggable(Log.VERBOSE)) {  
  11.     logClientCall(obj, method);  
  12. }  
  13.   
  14. Connection conn = ref.getChannel().newConnection();  
  15. RemoteCall call = null;  
  16. boolean reuse = true;  
  17. /* If the call connection is "reused" early, remember not to 
  18.  * reuse again. 
  19.  */  
  20. boolean alreadyFreed = false;  
  21. try {  
  22.     if (clientRefLog.isLoggable(Log.VERBOSE)) {  
  23.     clientRefLog.log(Log.VERBOSE, "opnum = " + opnum);  
  24.     }  
  25.     // create call context  
  26.     call = new StreamRemoteCall(conn, ref.getObjID(), -1, opnum);  
  27.     // marshal parameters  
  28.     try {  
  29.     ObjectOutput out = call.getOutputStream();  
  30.     marshalCustomCallData(out);  
  31.     Class[] types = method.getParameterTypes();  
  32.     for (int i = 0; i < types.length; i++) {   
  33.         marshalValue(types[i], params[i], out);  
  34.     }  
  35.     } catch (IOException e) {  
  36.     clientRefLog.log(Log.BRIEF,  
  37.         "IOException marshalling arguments: ", e);  
  38.     throw new MarshalException("error marshalling arguments", e);  
  39.     }  
  40.     // unmarshal return  
  41.     call.executeCall();  
  42.     try {  
  43.     Class rtype = method.getReturnType();  
  44.     if (rtype == void.class)  
  45.         return null;  
  46.     ObjectInput in = call.getInputStream();  
  47.       
  48.     /* StreamRemoteCall.done() does not actually make use 
  49.      * of conn, therefore it is safe to reuse this 
  50.      * connection before the dirty call is sent for 
  51.      * registered refs.   
  52.      */  
  53.     Object returnValue = unmarshalValue(rtype, in);  
  54.     /* we are freeing the connection now, do not free 
  55.      * again or reuse. 
  56.      */  
  57.     alreadyFreed = true;  
  58.     /* if we got to this point, reuse must have been true. */  
  59.     clientRefLog.log(Log.BRIEF, "free connection (reuse = true)");  
  60.     /* Free the call's connection early. */  
  61.     ref.getChannel().free(conn, true);  
  62.     return returnValue;  
  63.       
  64.     } catch (IOException e) {  
  65.     clientRefLog.log(Log.BRIEF,  
  66.              "IOException unmarshalling return: ", e);  
  67.     throw new UnmarshalException("error unmarshalling return", e);  
  68.     } catch (ClassNotFoundException e) {  
  69.     clientRefLog.log(Log.BRIEF,  
  70.         "ClassNotFoundException unmarshalling return: ", e);  
  71.     throw new UnmarshalException("error unmarshalling return", e);  
  72.     } finally {  
  73.     try {  
  74.         call.done();  
  75.     } catch (IOException e) {  
  76.         /* WARNING: If the conn has been reused early, 
  77.          * then it is too late to recover from thrown 
  78.          * IOExceptions caught here. This code is relying 
  79.          * on StreamRemoteCall.done() not actually 
  80.          * throwing IOExceptions.   
  81.          */  
  82.         reuse = false;  
  83.     }  
  84.     }  
  85. catch (RuntimeException e) {  
  86.     /* 
  87.      * Need to distinguish between client (generated by the 
  88.      * invoke method itself) and server RuntimeExceptions. 
  89.      * Client side RuntimeExceptions are likely to have 
  90.      * corrupted the call connection and those from the server 
  91.      * are not likely to have done so.  If the exception came 
  92.      * from the server the call connection should be reused. 
  93.      */  
  94.     if ((call == null) ||   
  95.     (((StreamRemoteCall) call).getServerException() != e))  
  96.            {  
  97.     reuse = false;  
  98.     }  
  99.     throw e;  
  100. catch (RemoteException e) {  
  101.     /* 
  102.      * Some failure during call; assume connection cannot 
  103.      * be reused.  Must assume failure even if ServerException 
  104.      * or ServerError occurs since these failures can happen 
  105.      * during parameter deserialization which would leave 
  106.      * the connection in a corrupted state. 
  107.      */  
  108.     reuse = false;  
  109.     throw e;  
  110. catch (Error e) {  
  111.     /* If errors occurred, the connection is most likely not 
  112.             *  reusable.  
  113.      */  
  114.     reuse = false;  
  115.     throw e;  
  116. finally {  
  117.     /* alreadyFreed ensures that we do not log a reuse that 
  118.      * may have already happened. 
  119.      */  
  120.     if (!alreadyFreed) {  
  121.     if (clientRefLog.isLoggable(Log.BRIEF)) {  
  122.         clientRefLog.log(Log.BRIEF, "free connection (reuse = " +  
  123.                    reuse + ")");  
  124.     }  
  125.     ref.getChannel().free(conn, reuse);  
  126.     }  
  127. }  
  128.    }  

2 RMI线程模型

  在JDK1.5及以前版本中,RMI每接收一个远程方法调用就生成一个单独的线程来处理这个请求,请求处理完成后,这个线程就会释放:

[java]  view plain copy
  1. Thread t = (Thread)  
  2.             java.security.AccessController.doPrivileged (  
  3.                 new NewThreadAction(new ConnectionHandler(socket,  
  4.                                   clientHost),  
  5.                         "TCP Connection(" + ++ threadNum +  
  6.                         ")-" + clientHost,  
  7.                         truetrue));  

   在JDK1.6之后,RMI使用线程池来处理新接收的远程方法调用请求-ThreadPoolExecutor。

  下面是一个简单的RMI程序的执行线程抓图,我们可以更好的了解RMI的线程机制。这个简单的RMI程序是服务端有一个远程方法实现,一个客户端同时请求执行这个远程方法100次。在JDK1.5中执行时生成的线程如下图所示,每个方法调用请求都是在一个单独的线程里执行,即 A Thread per Request。

JAVA RMI线程模型及内部实现机制_第1张图片

在JDK1.6中执行时生成的线程如下图所示,这些线程都是在ThreadPoolExecutor线程池中执行的。

JAVA RMI线程模型及内部实现机制_第2张图片

3 RMI线程池参数

  在JDK1.6中,RMI提供了可配置的线程池参数属性:

sun.rmi.transport.tcp.maxConnectionThread - 线程池中的最大线程数量

sun.rmi.transport.tcp.threadKeepAliveTime - 线程池中空闲的线程存活时间


原文: http://blog.csdn.net/sureyonder/article/details/5653609

你可能感兴趣的:(JAVA RMI线程模型及内部实现机制)