前几天把一个RMI应用迁移到新的机器,之前这个应用运行在一台增强版的PC上。
新机器上面的环境“理论上”跟以前的环境是一样的,至少我是这样要求服务器管理员的。
用同样的shell启动应用,问题发生了,应用并没像在之前的环境那样正常的运行,查log发现报RMI Exception
java.rmi.ServerException: RemoteException occurred in server thread
;
nested exception is:
java.rmi.UnmarshalException: error unmarshalling arguments ;
nested exception is:
java.lang.ClassNotFoundException: XXX_Stub
java.rmi.UnmarshalException: error unmarshalling arguments ;
nested exception is:
java.lang.ClassNotFoundException: XXX_Stub
请教了Mr. Smith,发现许多人遇到了类似的问题,解法也是五花八门。
有人说是环境属性CLASSPATH没添加那个XXX_Stub所在的JAR的路径,造成RMI REGISTRY无法找到这个XXX_Stub。
于是在启动的shell里面添加
export CLASSPATH
=
{path/to/JAR}
问题确认,再来检查新旧环境的user profile设置,果然发现新环境的user profile恰恰少了这个CLASSPATH的设定。在这里补上,就不用改启动的shell了。
不过接下来在另外一台机器上启动应用的Client测试RMI Server的时候却出现异常。
先还以为是新环境上charset的设置有问题,使RMI Server传回的xml中出现乱码,从而造成错误。
在这个方向上查了半天,还参考了车东大哥写的 Java中文处理学习笔记 ,后来才发现并非charset方面的问题,因为即使RMI Server产生的是正确的xml,也不能正确的传回Client。于是回过头来查Client的log,在Debug Log中发现这么一个错误:
java.rmi.ConnectException: Connection refused to host:
127.0.0.1
...
127.0.0.1
localhost.localdomain localhost hostalias
{true_ip} hostalias
{true_ip} hostalias
把第一行最后那个多余的hostalias拿掉,重启RMI Server,Client便可正常连接并操作了。
搞定收工!!
小结一下处理这次麻烦的体会。
- 基于RMI开发出来的应用在易用性上不如基于其他部分的Java平台的应用。特别是涉及到应用的跨平台迁移,比如Windows <--> Linux,则有许多牵涉到OS层面的设定问题。如针对RMI REGISTRY的CLASSPATH的设定,Linux上面etc/host 的设定。这些细节稍不留意,应用就无法正常运行,原因还不易发现。
- 借此机会复习了RMI 的通信模型:先是RMI Server向RMI REGISTRY注册并提交Stub,于是RMI Client就可在RMI REGISTRY中查找RMI Server的确切位置并下载Stub,最后RMI Client就依获取的Stub及RMI Server的确切位置跟RMI Server建立连接并执行操作。这次遇到的问题实际上都跟RMI REGISTRY有关。第1个问题是因为RMI REGISTRY在注册的时候没找到RMI Stub的确切位置,第2个问题则源于RMI REGISTRY在Linux上才会有的表现,当RMI Client向其查询RMI Server的确切位置的时候,它会去查 /etc/hosts 中的设定,并试图返回OS层面所设定的IP。自己对其中具体的逻辑还不了解,不过可以肯定的是,如果 /etc/hosts中设定错了,RMI Client将无法得到的正确的RMI Server的位置。
- 费了许多心思才把问题搞定,这次一定要让“粗心”的管理员兄弟请客!哈哈。
- 或许...我也要请一下这位兄弟的。
--END