RMI 绑定问题(从 Windows RMI 服务器到 Ubuntu RMI 注册表)

Posted

技术标签:

【中文标题】RMI 绑定问题(从 Windows RMI 服务器到 Ubuntu RMI 注册表)【英文标题】:RMI Binding Issue (from Windows RMI Server to Ubuntu RMI Registry) 【发布时间】:2010-10-27 07:07:15 【问题描述】:

我有一个 RMI 服务器,它在 localhost 上运行时正确绑定到 RMI 注册表(以证明设置正确)。执行此操作的代码是:

 private void exposeTickHistoryRemoteProvider(TickHistoryRemoteInterface aTickHistoryServer) 
        if (System.getSecurityManager() == null) 
            SecurityManager mySecurityManager = getSecurityManager();
   System.setSecurityManager(mySecurityManager);
        
  String rmiServerHostname = System.getProperties().getProperty("java.rmi.server.hostname");
  try 
   TickHistoryRemoteInterface stub =
                (TickHistoryRemoteInterface) UnicastRemoteObject.exportObject(aTickHistoryServer, 0);
            Registry registry = LocateRegistry.getRegistry(rmiServerHostname);
            String[] services = registry.list();
            registry.rebind(RMI_SERVICENAME_REUTERS_TICKHISTORY_SERVER, stub);
            log.info(RMI_SERVICENAME_REUTERS_TICKHISTORY_SERVER + " bound");
         catch (Exception e) 
         log.error(RMI_SERVICENAME_REUTERS_TICKHISTORY_SERVER + " exception:" + e.toString());
            e.printStackTrace();
        
    

我的 localhost 正在使用以下 Java 版本运行 Windows:

C:\eclipse>java -version
java version "1.6.0"
Java(TM) SE Runtime Environment (build 1.6.0-b105)
Java HotSpot(TM) Client VM (build 1.6.0-b105, mixed mode)

现在,我的问题是我想绑定到在另一台机器上运行的 RMIRegistry(运行 Ubuntu 10.04,使用 OpenJDK IcedTea6 1.8.1,java 版本 1.6.0_18)。

在这台 Ubuntu 机器上,我的 CLASSPATH (echo $CLASSPATH) 中没有任何内容,并且正在运行 OpenJDK RMIRegistry(与 Ubuntu 捆绑的相反):

sudo /usr/lib/jvm/java-6-openjdk/jre/bin/rmiregistry &

现在,在上面的代码中,当变量 rmiServerHostname 为“localhost”且 RMIRegistry 在我的 Windows localhost 上运行时,代码可以正常工作(RMI 服务器代码绑定到 RMI 注册表)。但是,当 rmiServerHostname 是我的远程 Ubuntu 机器(“神”)时,我在“重新绑定”调用中抛出以下异常:

 java.rmi.ServerException: RemoteException occurred in server thread; nested exception is: 
 java.rmi.UnmarshalException: error unmarshalling arguments; nested exception is: 
 java.lang.ClassNotFoundException: com.relative.tickhistory.provider.TickHistoryRemoteInterface
 java.rmi.ServerException: RemoteException occurred in server thread; nested exception is: 
 java.rmi.UnmarshalException: error unmarshalling arguments; nested exception is: 
 java.lang.ClassNotFoundException: com.relative.tickhistory.provider.TickHistoryRemoteInterface

如果我终止 RMIRegistry,我会收到不同的错误消息(我希望):

 java.rmi.ConnectException: Connection refused to host: deity; nested exception is: 
 java.net.ConnectException: Connection refused: connect
 at sun.rmi.transport.tcp.TCPEndpoint.newSocket(Unknown Source)
 at sun.rmi.transport.tcp.TCPChannel.createConnection(Unknown Source)
 at sun.rmi.transport.tcp.TCPChannel.newConnection(Unknown Source)
 at sun.rmi.server.UnicastRef.newCall(Unknown Source)
 at sun.rmi.registry.RegistryImpl_Stub.list(Unknown Source)

我会假设 RMIRegistry 的这些实现(Windows Java6 和 Ubuntu OpenJDK 6)之间没有不兼容......但是,我不确定如何深入了解这一点。特别是因为我知道代码可以正常工作(在第一个 Windows/localhost 示例中)。


目前的进展

非常感谢您提供有用的回复。我知道我对 rmiServerHostname(在我的本地主机上运行)和 rmiRegistryHostname(在“神”上运行)感到困惑。我已经修改了代码,如下所示,但仍然遇到同样的问题(注意“Registry registry = LocateRegistry.getRegistry(rmiRegistryHostname)”行中的更改):

    String rmiServerCodebase = System.getProperties().getProperty("java.rmi.server.codebase");
    String rmiServerHostname = System.getProperties().getProperty("java.rmi.server.hostname");
    String rmiRegistryHostname = "deity";
    System.out.println("rmiServerCodebase=" + rmiServerCodebase + "; rmiServerHostname=" + rmiServerHostname);
    try 
        TickHistoryRemoteInterface stub =
            (TickHistoryRemoteInterface) UnicastRemoteObject.exportObject(aTickHistoryServer, 0);
        Registry registry = LocateRegistry.getRegistry(rmiRegistryHostname);

print语句的输出是(注意,我的localhost是'RTPC-16')

"rmiServerCodebase=file:///C:/workspace/DEV/ReutersTickHistoryServer/ReutersTickHistoryInterface.jar; rmiServerHostname=RTPC-16"

此文件确实存在:

C:\>dir c:\workspace\DEV\ReutersTickHistoryServer\ReutersTickHistoryInterface.jar
 Volume in drive C is OS
 Volume Serial Number is 7AEB-A105

 Directory of c:\workspace\DEV\ReutersTickHistoryServer

22/10/2010  12:21 PM             9,467 ReutersTickHistoryInterface.jar
               1 File(s)          9,467 bytes

所以,再次总结一下:

当 RMIRegistry 和 RMIServer 位于同一物理主机(例如,本地主机)上时,此代码有效 当我尝试在单独的主机上运行 RMIRegistry 进程时会出现问题(即,RMIRegistry 在'神'上运行,而RMIServer 在我的本地主机上运行时,我希望它是这样的' RTPC-16') 我在客户端和服务器上捆绑了 RMI 接口代码库(“ReutersTickHistoryInterface.jar”),所以我没有预料到 RMI 需要传输任何类定义 - RMI 只需在客户端上创建存根类并处理实际 RMI 调用

【问题讨论】:

【参考方案1】:

你也误用了 java.rmi.server.hostname。那不是它的用途。由于此代码绑定到注册表,并且您只能在注册表在同一主机上运行时才能执行此操作,因此在获取用于绑定或取消绑定的注册表引用时,您应该只使用“localhost”。

【讨论】:

【参考方案2】:

您收到此异常是因为 rmiregistry 无法定位远程对象的存根或存根所需的其他类。启动服务器时需要指定java.rmi.server.codebase 属性,设置为实现存根的位置。这是必需的,以便可以将存根类动态下载到注册表。

有关此属性的更多详细信息,请查看Dynamic code downloading using RMI 教程。

【讨论】:

这不是“必需的”。这是一种解决方案。将类部署到客户端是另一种更常见的解决方案。见this answer。

以上是关于RMI 绑定问题(从 Windows RMI 服务器到 Ubuntu RMI 注册表)的主要内容,如果未能解决你的问题,请参考以下文章

Windows无法找到rmi注册表

RMI 系列(02)源码分析

服务器如何与 RMI 中的客户端建立会话

RMI 服务器如何响应多个 RMI 客户端调用?

使用 ProcessBuilder 时客户端无法连接 RMI 注册表

RMI : 确定远程对象的 IP 地址