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端口上的方法调用请求:
- 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 < types.length; i++) {
- params[i] = unmarshalValue(types[i], in);
- }
- } catch (java.io.IOException e) {
- throw new UnmarshalException(
- "error unmarshalling arguments", e);
- } catch (ClassNotFoundException e) {
- throw new UnmarshalException(
- "error unmarshalling arguments", e);
- } finally {
- call.releaseInputStream();
- }
- // make upcall on remote object
- Object result;
- try {
- result = method.invoke(obj, params);
- } catch (InvocationTargetException e) {
- throw e.getTargetException();
- }
- // marshal return value
- try {
- ObjectOutput out = call.getResultStream(true);
- Class rtype = method.getReturnType();
- if (rtype != void.class) {
- marshalValue(rtype, result, out);
- }
- } catch (IOException ex) {
- throw new MarshalException("error marshalling return", ex);
- /*
- * This throw is problematic because when it is caught below,
- * we attempt to marshal it back to the client, but at this
- * point, a "normal return" has already been indicated,
- * so marshalling an exception will corrupt the stream.
- * This was the case with skeletons as well; there is no
- * immediately obvious solution without a protocol change.
- */
- }
- } catch (Throwable e) {
- logCallException(e);
- ObjectOutput out = call.getResultStream(false);
- if (e instanceof Error) {
- e = new ServerError(
- "Error occurred in server thread", (Error) e);
- } else if (e instanceof RemoteException) {
- e = new ServerException(
- "RemoteException occurred in server thread",
- (Exception) e);
- }
- if (suppressStackTraces) {
- clearStackTraces(e);
- }
- out.writeObject(e);
- } finally {
- call.releaseInputStream(); // in case skeleton doesn't
- call.releaseOutputStream();
- }
- }
- protected static void marshalValue(Class type, Object value,
- ObjectOutput out)
- throws IOException
- {
- if (type.isPrimitive()) {
- if (type == int.class) {
- out.writeInt(((Integer) value).intValue());
- } else if (type == boolean.class) {
- out.writeBoolean(((Boolean) value).booleanValue());
- } else if (type == byte.class) {
- out.writeByte(((Byte) value).byteValue());
- } else if (type == char.class) {
- out.writeChar(((Character) value).charValue());
- } else if (type == short.class) {
- out.writeShort(((Short) value).shortValue());
- } else if (type == long.class) {
- out.writeLong(((Long) value).longValue());
- } else if (type == float.class) {
- out.writeFloat(((Float) value).floatValue());
- } else if (type == double.class) {
- out.writeDouble(((Double) value).doubleValue());
- } else {
- throw new Error("Unrecognized primitive type: " + type);
- }
- } else {
- out.writeObject(value);
- }
- }
- rotected static Object unmarshalValue(Class type, ObjectInput in)
- throws IOException, ClassNotFoundException
- {
- if (type.isPrimitive()) {
- if (type == int.class) {
- return new Integer(in.readInt());
- } else if (type == boolean.class) {
- return new Boolean(in.readBoolean());
- } else if (type == byte.class) {
- return new Byte(in.readByte());
- } else if (type == char.class) {
- return new Character(in.readChar());
- } else if (type == short.class) {
- return new Short(in.readShort());
- } else if (type == long.class) {
- return new Long(in.readLong());
- } else if (type == float.class) {
- return new Float(in.readFloat());
- } else if (type == double.class) {
- return new Double(in.readDouble());
- } else {
- throw new Error("Unrecognized primitive type: " + type);
- }
- } else {
- return in.readObject();
- }
- }
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)。
- 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 < types.length; i++) {
- marshalValue(types[i], params[i], out);
- }
- } catch (IOException e) {
- clientRefLog.log(Log.BRIEF,
- "IOException marshalling arguments: ", e);
- throw new MarshalException("error marshalling arguments", e);
- }
- // unmarshal return
- call.executeCall();
- try {
- Class rtype = method.getReturnType();
- if (rtype == void.class)
- return null;
- ObjectInput in = call.getInputStream();
- /* StreamRemoteCall.done() does not actually make use
- * of conn, therefore it is safe to reuse this
- * connection before the dirty call is sent for
- * registered refs.
- */
- Object returnValue = unmarshalValue(rtype, in);
- /* we are freeing the connection now, do not free
- * again or reuse.
- */
- alreadyFreed = true;
- /* if we got to this point, reuse must have been true. */
- clientRefLog.log(Log.BRIEF, "free connection (reuse = true)");
- /* Free the call's connection early. */
- ref.getChannel().free(conn, true);
- return returnValue;
- } catch (IOException e) {
- clientRefLog.log(Log.BRIEF,
- "IOException unmarshalling return: ", e);
- throw new UnmarshalException("error unmarshalling return", e);
- } catch (ClassNotFoundException e) {
- clientRefLog.log(Log.BRIEF,
- "ClassNotFoundException unmarshalling return: ", e);
- throw new UnmarshalException("error unmarshalling return", e);
- } finally {
- try {
- call.done();
- } catch (IOException e) {
- /* WARNING: If the conn has been reused early,
- * then it is too late to recover from thrown
- * IOExceptions caught here. This code is relying
- * on StreamRemoteCall.done() not actually
- * throwing IOExceptions.
- */
- reuse = false;
- }
- }
- } catch (RuntimeException e) {
- /*
- * Need to distinguish between client (generated by the
- * invoke method itself) and server RuntimeExceptions.
- * Client side RuntimeExceptions are likely to have
- * corrupted the call connection and those from the server
- * are not likely to have done so. If the exception came
- * from the server the call connection should be reused.
- */
- if ((call == null) ||
- (((StreamRemoteCall) call).getServerException() != e))
- {
- reuse = false;
- }
- throw e;
- } catch (RemoteException e) {
- /*
- * Some failure during call; assume connection cannot
- * be reused. Must assume failure even if ServerException
- * or ServerError occurs since these failures can happen
- * during parameter deserialization which would leave
- * the connection in a corrupted state.
- */
- reuse = false;
- throw e;
- } catch (Error e) {
- /* If errors occurred, the connection is most likely not
- * reusable.
- */
- reuse = false;
- throw e;
- } finally {
- /* alreadyFreed ensures that we do not log a reuse that
- * may have already happened.
- */
- if (!alreadyFreed) {
- if (clientRefLog.isLoggable(Log.BRIEF)) {
- clientRefLog.log(Log.BRIEF, "free connection (reuse = " +
- reuse + ")");
- }
- ref.getChannel().free(conn, reuse);
- }
- }
- }
2 RMI线程模型
在JDK1.5及以前版本中,RMI每接收一个远程方法调用就生成一个单独的线程来处理这个请求,请求处理完成后,这个线程就会释放:
- Thread t = (Thread)
- java.security.AccessController.doPrivileged (
- new NewThreadAction(new ConnectionHandler(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线程池中执行的。
3 RMI线程池参数
在JDK1.6中,RMI提供了可配置的线程池参数属性:
sun.rmi.transport.tcp.maxConnectionThread - 线程池中的最大线程数量
sun.rmi.transport.tcp.threadKeepAliveTime - 线程池中空闲的线程存活时间
转自:http://blog.csdn.net/sureyonder/article/details/5653609
相关推荐
8. **安全性(Security)**:RMI支持基于Java Security Manager的安全模型,可以通过设置策略文件控制客户端和服务器之间的权限。 9. **性能优化(Performance Optimization)**:RMI提供了许多优化手段,如持久化...
RMI是Java 1.1及更高版本的标准组成部分,确保了在任何支持JVM的系统间无缝通信。 1.1 背景 分布计算是指程序、数据和计算分布在广泛网络上的编程模式,旨在平衡计算资源,适应应用程序需求。分布式对象技术,如...
4. **序列化**:由于Java对象不能直接在网络上传输,RMI使用Java的序列化机制将对象转换为字节流,然后在网络上传输,到达目的地后再反序列化恢复。 5. **异常处理**:RMI支持跨网络的异常处理,如果远程方法抛出...
综上所述,这个上机实验旨在让学生掌握Java中实现客户端-服务器通信、构建多线程服务器以及利用RMI进行远程调用的关键技能。通过实践,学生将能够理解这些核心概念,并能够在实际项目中应用。文件列表中的"客户端...
6. **安全性**:RMI支持标准的Java安全模型,可以控制客户端对远程对象的访问权限。通过策略文件,可以设定远程方法调用的安全策略。 7. **异常处理**:由于网络通信的不可靠性,RMI调用可能会抛出各种异常,如...
多线程模型则允许多个请求同时处理,提高了服务器的并发能力。在实现多线程Web服务器时,我们需要创建一个主线程池,用于接收新的连接请求,然后将每个请求分配给一个工作线程来处理。每个工作线程负责读取请求、...
Java Remote Method Invocation (RMI) 是Java编程语言中的一种机制,允许在不同的Java虚拟机(JVM)之间进行远程调用。"java-rmi.zip_RMI java_rmi"这个文件很可能是包含了一些关于RMI实现的示例代码或库文件。RMI在...
由于RMI是Java平台的标准组成部分,它在所有1.1及更高版本的Java虚拟机中都可用,且所有RMI系统使用相同的公开协议,实现了跨Java系统间的无缝通信。 综上所述,Java RMI是Java平台中实现分布式计算的一种强大工具...
本资源是一个关于Java RMI的英文书籍,虽然没有标签,但可以预期它会涵盖RMI的基础概念、实现机制以及实际应用。 在Java RMI中,核心概念包括: 1. **远程接口(Remote Interface)**:这是定义远程方法的接口,...
RMI,即远程方法调用,是Java平台提供的分布式计算模型,它允许Java对象跨网络进行方法调用,如同本地调用一样。RMI的实现基于Java的面向对象特性,能够传递完整的对象作为参数和返回值,而不仅仅是预定义的数据类型...
RMI是Java提供的一种机制,允许在不同JVM之间透明地调用对象的方法,就像这些对象都在同一台机器上一样。RMI的核心组件包括远程接口、远程实现和注册表。在"RMIClientFactory.java"中,可能包含了创建RMI客户端连接...
Java Remote Method Invocation (RMI) 是Java平台提供的一种机制,允许在不同的Java虚拟机之间进行对象的方法调用,跨越网络连接。RMI是Java分布式计算的核心技术,它使得开发者能够构建分布式应用程序,使得代码...
除JVM外,许多Java标准库组件,如AWT、Swing、servlet容器、应用程序服务器和RMI等,也会在内部创建和管理线程。 线程之所以在程序设计中扮演关键角色,主要是由于以下几个原因: 1. 提高用户界面(UI)的响应性:...
服务器需要监听特定端口,接收到请求后,创建新的线程来处理,从而避免了单线程模型中的阻塞问题。此外,还需注意线程安全和资源管理,防止死锁和竞态条件等并发问题。 最后,RMI(Remote Method Invocation)是...
java.util 包含 collection 框架、遗留的 collection 类、事件模型、日期和时间设施、国际化和各种实用工具类(字符串标记生成器、随机数生成器和位数组)。 java.util.concurrent 在并发编程中很常用的实用工具类...
1. **并发编程**:深入解析Java并发模型,包括线程安全、并发控制、死锁避免等。 2. **多线程实践**:如何设计和实现高效的多线程程序,包括线程池的使用和优化。 3. **分布式架构**:介绍分布式系统的设计原则,...
Java的远程方法调用(Remote Method Invocation,简称RMI)是Java平台中一种用于实现分布式计算的技术。它允许Java对象在不同的JVM之间进行交互,仿佛这些对象都在同一个进程中一样。RMI是Java企业版(Java EE)的一...
远程方法调用(Remote Method Invocation,RMI)是Java编程语言中的一种机制,它允许一个对象的方法被运行在不同JVM(Java Virtual Machine)上的另一个对象调用。这个标题"RMI.rar_remote"暗示了这是一个关于Java ...