【问题标题】:RMI and JMX Socket FactoriesRMI 和 JMX 套接字工厂
【发布时间】:2014-02-04 14:52:35
【问题描述】:

我正在尝试在我的 java 应用程序中启动嵌入式 JMX 服务器。我想为 RMI 注册表和实际的 RMI 流量(或 JMX 流量,如果你愿意)使用相同的端口。显然这是可能的,因为RMI Registry is merely a Remote Object itself。

另外的困难是我需要使用 Socket Factories,因为我需要绑定到特定的 NIC。 我开始:

int registryPort = 3012;
int jmxPort = 3012;    // use the same port

这是我的服务器套接字工厂。非常直接的东西:

public class MyRMIServerSocketFactory implements RMIServerSocketFactory {

    private final InetAddress inetAddress;

    public MyRMIServerSocketFactory(InetAddress inetAddress) {
        this.inetAddress = inetAddress;
    }

    @Override
    public ServerSocket createServerSocket(int port) throws IOException {
        return new ServerSocket(port, 0, inetAddress);
    }

    @Override
    public int hashCode() {
        int hash = 5;
        hash = 97 * hash + (this.inetAddress != null ? this.inetAddress.hashCode() : 0);
        return hash;
    }

    @Override
    public boolean equals(Object obj) {
        if (obj == null) {
            return false;
        }
        if (getClass() != obj.getClass()) {
            return false;
        }
        final FlexibleRMIServerSocketFactory other = (FlexibleRMIServerSocketFactory) obj;
        if (this.inetAddress != other.inetAddress && (this.inetAddress == null || !this.inetAddress.equals(other.inetAddress))) {
            return false;
        }
        return true;
    }    
}

(equals() 和 hashCode() 是由我的 IDE 自动生成的,不要卡在它们上面)

我这样创建 RMI 注册表:

serverSocketFactory = new MyRMIServerSocketFactory(inetAddressBind);
LocateRegistry.createRegistry(
        registryPort,
        RMISocketFactory.getDefaultSocketFactory(),  // client socket factory
        serverSocketFactory // server socket factory
        );       

然后创建 JMXConnectorServer:

JMXServiceURL url = new JMXServiceURL(
     "service:jmx:rmi://localhost:" + jmxPort + 
      "/jndi/rmi://:" + registryPort + "/jmxrmi");

Map env = new HashMap();
env.put(RMIConnectorServer.RMI_SERVER_SOCKET_FACTORY_ATTRIBUTE, serverSocketFactory);

connector = JMXConnectorServerFactory.newJMXConnectorServer(
                url, 
                env,
                ManagementFactory.getPlatformMBeanServer());

connector.start();

这会导致connector.start() 出现绑定错误,指出该地址已在使用中。

如果我完全跳过使用套接字工厂:

LocateRegistry.createRegistry(registryPort);

和

JMXServiceURL url = new JMXServiceURL(
     "service:jmx:rmi://localhost:" + jmxPort + 
      "/jndi/rmi://:" + registryPort + "/jmxrmi");


connector = JMXConnectorServerFactory.newJMXConnectorServer(
                url, 
                null,
                ManagementFactory.getPlatformMBeanServer());

connector.start();

它按预期工作,即只打开一个端口并且没有错误。

问题:如何让 'single-listening-port-scenario' 与 Socket Factories 一起工作?

更新 - 最终解决方案

如果您使用空客户端套接字工厂创建注册表,它会起作用,即

LocateRegistry.createRegistry(
        registryPort,
        null,  // client socket factory (let it default to whatever RMI lib wants)
        serverSocketFactory // server socket factory
        );       

我还必须设置java.rmi.server.hostname System Property,我猜在像我这样的场景中经常会出现这种情况。

【问题讨论】:

  • 您可以考虑使用 SimpleJMX,它可以为您完成很多工作。 256.com/sources/simplejmx
  • 我会很高兴,但上次我看它没有使用套接字工厂的功能。我错了吗?
  • 背景:没有套接字工厂 RMI/JMX 有点用处,除了在最简单的网络设置上。在企业世界中,主机通常是多宿主的。这就是为什么 ! :-)
  • 啊有趣。我得补充一下。
  • 为什么需要绑定特定的网卡?

标签: java rmi jmx


【解决方案1】:

这应该有效:您的 ServerSocketFactory 中有一个看起来正确的 equals() 方法,这是重要的一点。 RMI 确实这么称呼。但是,目前这不适用于您的客户端套接字工厂。您需要将null 作为客户端套接字工厂传递,而不是RMISocketFactory.getDefaultSocketFactory(),,因为它会为您提供sun.rmi.transport.proxy.RMIMasterSocketFactory,,由于某种原因它没有实现equals()。或者你自己用一个合理的equals() 方法实现RMIClientSocketFactory。

所以这里发生的情况是,RMI 首先比较 CSF,结果它们是不相等的,所以它甚至不用比较 SSF:

csf1.equals(csf2) && ssf1.equals(ssf2)

所以它会尝试在你指定的端口上创建一个新的ServerSocket,这与第一个端口相同,所以它失败了。

您可以在 equals 的开头添加一个快捷方式,如果 this == that 则返回 true。

【讨论】:

  • 是的,由于某种原因,JMX 代码调用 createServerSocket(...) 两次,但在第二次绑定时失败。当您不使用自定义套接字工厂@nolan6000 时,我无法弄清楚有什么不同。
  • 哦,根据我的调试器,它似乎没有调用 hasCode() 和 equals() 方法。
  • 好吧,我可以告诉你,我已经尝试让套接字工厂更智能一些,以便安全地保存它创建的套接字。然后在第二次调用时,它将简单地返回与第一次调用时创建的相同的套接字。就服务器端而言,这有效(启动阶段没有错误)。但是客户端因“java.rmi.NoSuchObjectException: no such object in table”而惨遭失败,服务器端在连接尝试时也得到“java.io.IOException: unknown transport op 116”。
  • 我想我明白了(遵循 EJP 的建议):在 createRegistry() 调用上将客户端套接字工厂设置为 null。
  • @Gray。如果你能重现我的成功,请告诉我。如果是这样,我会在 EJP 的答案上打上绿色的小复选标记。
【解决方案2】:

您应该搜索 JMXMP 协议以及包含它的 jmxremote_optional.jar。这是一种更可控、更高效的 JMX 协议。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-07-03
    • 2012-09-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-10-25
    相关资源
    最近更新 更多