java - RMI 和 JMX 套接字工厂

标签 java rmi jmx

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

增加的困难是我需要使用套接字工厂,因为我需要绑定(bind)到特定的 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() 出现绑定(bind)错误,表明该地址已被使用。

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

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();

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

问题:如何使“单监听端口场景”与套接字工厂一起工作?

更新 - 最终解决方案

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

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我想在像我这样的场景中经常会出现这种情况。

最佳答案

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

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

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

因此它会尝试在您指定的端口上创建一个新的 ServerSocket,这与第一个端口相同,因此失败。

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

关于java - RMI 和 JMX 套接字工厂,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/21555710/

相关文章:

java - 是否有(实验性的)启用 JSR-262 JMX-WS 的 Java 工具或应用程序?

java - 将值从 Servlet 传递到我的 JSP

java - jackson 映射器 : Unable to convert JSON object to Java Singleton class

使用隧道的 Java RMI - 一个异常(exception)

java - 是否可以在 RMI 中通过引用传递?

java - 系统间连接异常

java - "onPreferenceStartFragment()"单击首选项时未调用方法

java - 如何在具有非模块化依赖项的 Java 11 中打包应用程序

spring - JMX 对象如何映射到 SNMP OID?

jakarta-ee - 如何暂停/暂停消息驱动的 bean 的使用?