rmi端口问题

RMI之所以使用的范围受限制主要有两方面原因,其一:必须要是java,平台的异构性受到限制;其二:穿越防火墙不方便。这里主要谈谈RMI如何通过固定分配端口来穿越防火墙。     RMI穿越防火墙不方便主要是因为除了RMI服务注册的端口(默认1099)外,与RMI的通讯还需要另外的端口来传送数据,而另外的端口是随机分配的,所以要想RMI的客户能通过防火墙来与RMI服务通讯,则需要能让随机分配的端口固定下来,具体做法如下:

1、增加一个文件SMRMISocket.java

import   java.rmi.server.*;   

import   java.io.*;   

import   java.net.*;   

public   class   SMRMISocket   extends   RMISocketFactory   {  

          public   Socket   createSocket(String   host,   int   port)  throws   IOException{   

                  return   new   Socket(host,port);   

          }   

          public   ServerSocket   createServerSocket(int   port)   throws   IOException   {   

                  if   (port   ==   0)   

                          port   =   10990; //不指定就随机分配了  

                  return   new   ServerSocket(port);   

          }

  }     

  2、在实例化UnicastRemoteObject的子类前加入一下代码:

[java] view plaincopyprint?

    try { 

    RMISocketFactory.setSocketFactory(new SMRMISocket()); 

} catch (Exception ex) {  

这样的话RMI分配的端口就被固定了,防火墙只需要打开1099和10990端口即可。

 

 ---------------------------------------------------------------------------------------------------------------------------------

 

在用RMI做服务监控的时候碰到两个问题:

1. InetAddress.getLocalHost().getHostAddress(); 报错UnknowHost

linux /etc/hosts 加主机名以及映射

hostname hostname -i 能正确获取到就ok

 

----------------------------------------------------------------------------------------------------------------------------------

 

2. 先记录问题

1) 服务端重启客户端报错的问题

http://forum.springsource.org/showthread.php?t=61575

at monitor.SendHeartBeatThread.run(SendHeartBeatThread.java:32)

Caused by: java.rmi.NoSuchObjectException: no such object in table

        at sun.rmi.transport.StreamRemoteCall.exceptionReceivedFromServer(StreamRemoteCall.java:275)

        at sun.rmi.transport.StreamRemoteCall.executeCall(StreamRemoteCall.java:252)

        at sun.rmi.server.UnicastRef.invoke(UnicastRef.java:161)

        at org.springframework.remoting.rmi.RmiInvocationWrapper_Stub.invoke(Unknown Source)

        at org.springframework.remoting.rmi.RmiClientInterceptor.doInvoke(RmiClientInterceptor.java:397)

        at org.springframework.remoting.rmi.RmiClientInterceptor.doInvoke(RmiClientInterceptor.java:343)

        ... 5 more

<bean id="beanId" class="org.springframework.remoting.rmi.RmiProxyFa ctoryBean">
<property name="serviceUrl" value="rmi://127.0.0.1:6666/appName"/>
<property name="serviceInterface" value="test.HelloI"/>
<!--property name="cacheStub" value="false"/-->
<property name="lookupStubOnStartup" value="false"/>
<property name="refreshStubOnConnectFailure" value="true"/>
</bean>

因为RMI stub被连接到特定的端点,不仅仅是为每个调用打开一个给定的目标地址的连接,所以如果重新启动RMI端点主机的服务器,那么就需要重新注册这些stub,并且客户端需要再次查询它们。

     虽然目标服务的重新注册在重新启动时通常会自动发生,不过此时客户端保持的stub将会变的陈旧,且客户端不会注意这些,除非他们再次尝试调用stub上的方法,而这也将throw一个连接失败的异常。
      为了避免这种情形,Spring的RmiProxyFactoryBean提供了一个refreshStubOnConnectFailure的bean属性,如果调用失败,并且连接异常的话,将它设定为true来强制重新自动查询stub。
<bean id="reportService"
  class="org.springframework.remoting.rmi.RmiProxyFactoryBean">
  <property name="serviceUrl">
   <value>${showcasewiz.report.serviceurl}</value>
  </property>
  <property name="serviceInterface">
   <value>com.meetexpo.showcase.backend.service.ReportService</value>
  </property>
  <property name="refreshStubOnConnectFailure">
    <value>true</value>
  </property>
 </bean>
      stub查询的另一个问题是,目标RMI服务器和RMI注册项在查询时要为可用的。如果客户端在服务器启动之前,尝试查询和缓存该服务stub,那么客户端的启动将会失败(即使还不需要该服务)。
     为了能够惰性查询服务stub,设定RmiProxyFactoryBean的lookupStubOnStarup标志为false。然后在第一次访问时查询该stub,也就是说,当代理上的第一个方法被调用的时候去主动查询stub,同时被缓存。这也有一个缺点,就是直到第一次调用,否则无法确认目标服务是否实际存在。
<bean id="reportService"
  class="org.springframework.remoting.rmi.RmiProxyFactoryBean">
  <property name="serviceUrl">
   <value>${showcasewiz.report.serviceurl}</value>
  </property>
  <property name="serviceInterface">
   <value>com.meetexpo.showcase.backend.service.ReportService</value>
  </property>
  <property name="lookupStubOnStartup">
     <value>false</value>
  </property>
  <property name="refreshStubOnConnectFailure">
    <value>true</value>
  </property>
 </bean>
还有一个属性就是cacheStub,当它设置为false的时候,就完全避免了stub的缓存,但影响了性能。需要的时候还是可以试试。

 ----------------------------------------------------------------------------------------------------------------------------------

2) 内网(局域网)间访问明明指定一个ip确变成另一个ip的情况)

Exception in thread "main" org.springframework.remoting.RemoteConnectFailureException: Cannot connect to remote service [rmi://218.206.203.3:9902/cmpayNotice]; nested exception is java.rmi.ConnectException: Connection refused to host: 10.97.164.26; nested exception is: 
    java.net.ConnectException: Connection timed out: connect
Caused by: java.rmi.ConnectException: Connection refused to host: 10.97.164.26; nested exception is: 
    java.net.ConnectException: Connection timed out: connect

 

 

以下from:http://dd2086.iteye.com/blog/362995

出错堆栈:
org.springframework.remoting.RemoteConnectFailureException: Cannot connect to remote service [rmi://192.168.2.61/Service]; nested exception is java.rmi.ConnectException: Connection refused to host: 192.168.7.11; nested exception is: 
java.net.ConnectException: Connection timed out: connect 

很奇怪,rmi访问的url是对的,但是后续的访问得到的IP却是双网卡中的另外一个无法访问的。

在本机做个简单试验:
  System.out.println(UnicastRemoteObject.exportObject(new Remote(){},0));
输出:
Proxy[Remote,RemoteObjectInvocationHandler[UnicastRef [liveRef: [endpoint:[192.168.2.3:2339](local),objID:[0]]]]]
说明对象在没有注册出去的时候其IP就已经决定,绑定出去以后客户端就按照这个IP按图索骥。Server端有两个网卡,乱找一个就Over了。
这种方式对分布式对象注册很有用,但是碰到多IP的情况下就比较弱智了。sun的faq上给出一个简单答案:
http://java.sun.com/j2se/1.5.0/docs/guide/rmi/faq.html#netmultihomed

一句话:
  System.setProperty("java.rmi.server.hostname","192.168.2.3");//建议从配置文件加载。

  或

  java -Djava.rmi.server.hostname=xxx -jar xxx.jar

 

http://yangwencan2002.iteye.com/blog/284249

http://hi.baidu.com/wolf55/item/1676aa0b41184417cd34ea52

猜你喜欢

转载自tcxiang.iteye.com/blog/2066475
Rmi