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
相关文章
|
7月前
|
Dubbo Java 应用服务中间件
从源码全面解析 dubbo 服务端服务调用的来龙去脉
从源码全面解析 dubbo 服务端服务调用的来龙去脉
|
3月前
|
网络协议 安全 Java
分布式(基础)-RMI的原理
分布式(基础)-RMI的原理
|
4月前
|
前端开发 Java API
一文教会你如何简单使用Fegin进行远程服务调用
这篇文章介绍了如何在分布式微服务架构中使用Feign进行远程服务调用,包括Feign的基本介绍、使用步骤,以及在项目中的实际运用方法,并通过测试验证了调用远程服务的成功性。
一文教会你如何简单使用Fegin进行远程服务调用
|
存储 缓存 负载均衡
计网 - 怎样实现 RPC 框架
计网 - 怎样实现 RPC 框架
110 0
|
缓存 NoSQL 网络协议
|
编解码 负载均衡 Java
【Fegin技术专题】「原生态」从源码层面让你认识Feign工作流程和运作机制
【Fegin技术专题】「原生态」从源码层面让你认识Feign工作流程和运作机制
271 0
【Fegin技术专题】「原生态」从源码层面让你认识Feign工作流程和运作机制
|
负载均衡 Java Maven
【Fegin技术专题】「原生态」打开Fegin之RPC技术的开端,你会使用原生态的Fegin吗?(下)
【Fegin技术专题】「原生态」打开Fegin之RPC技术的开端,你会使用原生态的Fegin吗?(下)
136 0
|
XML JSON 安全
【Fegin技术专题】「原生态」打开Fegin之RPC技术的开端,你会使用原生态的Fegin吗?(中)
【Fegin技术专题】「原生态」打开Fegin之RPC技术的开端,你会使用原生态的Fegin吗?(中)
150 0