RMI原理一记远程调用

简介:

JDK1.5以前RMI调用是需要存根与代理的,1.2之后代理类好像看不到了.rmic只会生成存根类.

(1.2之前的JDK,我也没试过,我学习JAVA的时候,1.5就出来了)

开发RMI应用时,在进行bind对象时,会检测远程对象所对应的存根是否存在.这就是常发生的

*_stub.class找不到的问题. STUB用在客户端调用时,Rmi Registry为什么要检测他呢?

这是因为当客户端通过Naming.lookup获取这个远程对象时, Registry会把这个存根对象

或用于生成存根对象meta发给客户端,客户端通过这个存根对象或者通过meta生成存根对象.

进而进行运程对象的方法调用.


JDK1.5之后,有了新变化: Naming.lookup获取不再是这个存根对象,而是一个动态代理类.

这里只简单描述一下过程:


1. 获取的代理对象, 其InvocationHandler为RemoteObjectInvocationHandler:

  public class RemoteObjectInvocationHandler extends RemoteObject implements InvocationHandler


2. RemoteObjectInvocationHandler的方法为:

1
2
3
4
5
6
7
8
9
public  Object invoke(Object proxy, Method method, Object[] args)
    throws  Throwable
   {
       if  (method.getDeclaringClass() == Object. class ) {
        return  invokeObjectMethod(proxy, method, args);
        else  {
        return  invokeRemoteMethod(proxy, method, args);
       }
  }

这里把调用分为两部分:invokeObjectMethod和invokeRemoteMethod.我们主要看

invokeRemoteMethod:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
private  Object invokeRemoteMethod(Object proxy,
                       Method method,
                       Object[] args)
     throws  Exception
     {
     try  {
         if  (!(proxy  instanceof  Remote)) {
         throw  new  IllegalArgumentException(
             "proxy not Remote instance" );
         }
         return  ref.invoke((Remote) proxy, method, args,
                   getMethodHash(method));
     catch  (Exception e) {
         if  (!(e  instanceof  RuntimeException)) {
         Class<?> cl = proxy.getClass();
         try  {
             method = cl.getMethod(method.getName(),
                       method.getParameterTypes());
         catch  (NoSuchMethodException nsme) {
             throw  (IllegalArgumentException)
             new  IllegalArgumentException().initCause(nsme);
         }
         Class<?> thrownType = e.getClass();
         for  (Class<?> declaredType : method.getExceptionTypes()) {
             if  (declaredType.isAssignableFrom(thrownType)) {
             throw  e;
             }
         }
         e =  new  UnexpectedException( "unexpected exception" , e);
         }
         throw  e;
     }
     }

主要通过ref.invoke来进行调用,若打开rmic生成的存根类,你会发现调用方法也是一样的。

这个动态代理类,其实就是存根的替代品。这里ref的类为sun/rmi/server/UnicastRef.java

签名为:public class UnicastRef implements RemoteRef, 其invoke方法实现如下:


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
public  Object  invoke(Remote obj,
                       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);
          }
      }
  }


本文转自 anranran 51CTO博客,原文链接:http://blog.51cto.com/guojuanjun/1350826
相关文章
|
消息中间件 XML JSON
一文就读懂RPC远程调用核心原理
rpc的全称是Remote Procedure Call,即远程过程调用,是分布式系统的常用通信方法。 Remote,简单来说的话就是两个不同的服务之间,两个服务肯定是两个不同的进程。因此,我们就从跨进程进行访问的角度去理解就行了。 Procedure,意思是一串可执行的代码,我们写Java的方法,就是一段课程行的代码。 Call,即调用,调用的就是跨了进程的方法。
295 0
一文就读懂RPC远程调用核心原理
|
存储 缓存 负载均衡
计网 - 怎样实现 RPC 框架
计网 - 怎样实现 RPC 框架
90 0
|
缓存 NoSQL 网络协议
|
JSON Java API
【Fegin技术专题】「原生态」打开Fegin之RPC技术的开端,你会使用原生态的Fegin吗?(上)
【Fegin技术专题】「原生态」打开Fegin之RPC技术的开端,你会使用原生态的Fegin吗?(上)
109 0
|
XML JSON 安全
【Fegin技术专题】「原生态」打开Fegin之RPC技术的开端,你会使用原生态的Fegin吗?(中)
【Fegin技术专题】「原生态」打开Fegin之RPC技术的开端,你会使用原生态的Fegin吗?(中)
126 0
|
负载均衡 Java Maven
【Fegin技术专题】「原生态」打开Fegin之RPC技术的开端,你会使用原生态的Fegin吗?(下)
【Fegin技术专题】「原生态」打开Fegin之RPC技术的开端,你会使用原生态的Fegin吗?(下)
112 0
|
存储 安全 API
【Fegin技术专题】「原生态」打开Fegin之RPC技术的开端,你会使用原生态的Fegin吗?(高级用法)
【Fegin技术专题】「原生态」打开Fegin之RPC技术的开端,你会使用原生态的Fegin吗?(高级用法)
130 0
|
JSON Dubbo 网络协议
|
缓存 编解码 监控
看大牛是如何一次性把RPC远程过程调用,Dubbo架构进阶给讲清的
Dubbo架构进阶 Dubbo架构主要包含四个角色:消费者、提供者、注册中心和监控系统。
看大牛是如何一次性把RPC远程过程调用,Dubbo架构进阶给讲清的