rmi.ConnectException: Connection refused to host: 127.0.0.1前因后果
又是Linux下RMI异常,不过这次是单网卡的情况,先看异常信息:
[appframe] 2012-10-09 15:20:59,217 - com.opensymphony.xwork2.DefaultActionInvocation -com.opensymphony.xwork2.DefaultActionInvocation.invokeAction(DefaultActionInvocation.java:383) -2603911 [http-8282-12] DEBUG - Executing action method = haveDataContentView org.springframework.remoting.RemoteConnectFailureException: Could not connect to remote service [rmi://192.168.2.74:7777/ViewService]; nested exception is java.rmi.ConnectException: Connection refused to host: 127.0.0.1; nested exception is: java.net.ConnectException: Connection refused at org.springframework.remoting.rmi.RmiClientInterceptorUtils.convertRmiAccessException(RmiClientInterceptorUtils.java:189) at org.springframework.remoting.rmi.RmiClientInterceptor.doInvoke(RmiClientInterceptor.java:347) at org.springframework.remoting.rmi.RmiClientInterceptor.invoke(RmiClientInterceptor.java:259) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172) at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:202) at $Proxy65.viewDictContentHtml(Unknown Source) at com.plat.sap.action.SummaryPropertyDataAction.haveDataContentView(Unknown Source)
主要信息为:java.rmi.ConnectException:Connectionrefusedtohost:127.0.0.1
经查,发现Server应用和Client应用都部署在单网卡的Linux上,且Server调Client端的RMI服务没问题,所以料到异常跟系统的域名或IP配置有关。通过Google异常,发现下面这篇文章,谢谢它最终指引我们解决了问题。现收藏如下:
java.rmi.ConnectException:Connectionrefusedtohost:127.0.0.1异常主要根源是spring实现中,server端使用了主机名,linux在解析主机名时使用了与windows不同的逻辑。
在使用主机名时有两种说法:
说法一、在server端返回的绑定对象中采用的是server主机名(hostname),写一个rmi客户端程序,你可能会收到如标题这样的异常。这个问题其实是由rmi服务器端程序造成的。客户端程序向服务端请求一个对象的时候,返回的stub对象里面包含了服务器的hostname,客户端的后续操作根据这个hostname来连接服务器端。要想知道这个hostname具体是什么值可以在服务器端bash中打入指令:
hostname-i
如果返回的是127.0.0.1,那么你的客户端肯定会抛如标题的异常了。
解决这个问题有两个方式:
1)修改/etc/hosts
找到127.0.0.1hostxxxxx这样的字样。把127.0.0.1改成真实的,可供其他机器连接的ip。
这样客户端就能得到真实的ip了。
2)在rmi服务器端程序启动脚本中加上两行,显式指定hostname。我的脚本:
hostname=`hostname` java -cp $CLASSPATH -Djava.rmi.server.codebase=$codebase -Djava.security.policy=$PROJECT_HOME/se_server/conf/se_server.policy - Djava.rmi.server.hostname=$hostname com.abc.server.StartServer > $PROJECT_HOME/se_server/logs/init.log 2>&1 &
不过该方式有个局限,其他机器肯定能识别ip,但是可能无法识别hostname。当然,你也可以直接写死这个hostname,比如:-Djava.rmi.server.hostname=xxx.xxx.xxx.xxx。这样最省力,就是缺乏灵活性。
说法二、返回的是根据主机名对应的ip
Linux系统使用/etc/hosts文件中localhost解析ip为127.0.0.1,当客户端向服务器Lookup时,服务端就会把解析出来的地址发给客户端,让客户端再根据这个地址去连接,客户端收到127.0.0.1这个地址,也使用/etc/hosts文件中localhost解析ip去连接,实际连接的是自己本身,当然也就不行了。
我把服务器的IP地址加到服务器的/etc/hosts文件中,并放在127.0.0.1之前,以让该服务能先解析到这个IP,从而正确解析出来机器名所对应的IP。
举例:
在服务端的Naming.rebind("SectionWorkerManager",manager);没有指定ip,(这个语句在Windows下没问题)linux系统自己使用localhost解析为IP127.0.0.1,当客户端向服务器Lookup时,服务端就会把解析出来的地址发给客户端,让客户端再根据这个地址去连接,客户端收到127.0.0.1这个地址去连接,实际连接的是自己本身,当然也就不行了。
更正办法:把Naming.rebind("SectionWorkerManager",manager);
改成Naming.rebind("rmi://10.1.5.xxx:1099/SectionWorkerManager",manager);,直接用IP地址(10.1.5.xxx:1099为服务器本身IP),这样就没问题了;
或者是用机器名,该服务器的名字为RHELTEST,把它加到服务器的hosts文件中,并放在127.0.0.1之前,以让该服务能正确解析出来机器名所对应的IP;要么用域名解析也行,这种方法比较适合大规模场合。
在Windows下能正常工作,在linux下却不行,这可能是操作系统解析localhost为ip时时的机制不一样引起的。
在redhates5中测试,应该使用的是方法2。
不过两种方式都能解决该问题,采用哪种方式,根据服务器可做的修改来决定。
springrmi对此的特别说明:
Note:RMImakesabest-effortattempttoobtainthefullyqualifiedhostname.Ifonecannotbedetermined,itwillfallbackandusetheIPaddress.Dependingonyournetworkconfiguration,insomecasesitwillresolvetheIPtotheloopbackaddress.ToensurethatRMIwillusethehostnameboundtothecorrectnetworkinterface,youshouldpassthejava.rmi.server.hostnamepropertytotheJVMthatwillexporttheregistryand/ortheserviceusingthe"-D"JVMargument.Forexample:-Djava.rmi.server.hostname=myserver.com。
全文转载完毕。
以前还整理过一个双网卡下RMI配置的问题,mark一下,有空弄到网上来和大家分享。