RMI和JMX套接字工厂

我正在尝试在我的Java应用程序中启动嵌入式JMX服务器。 我想为RMI注册表和实际的RMI流量使用相同的端口(如果你愿意,可以使用JMX流量)。 显然这是可能的,因为RMI注册表本身只是一个远程对象 。

增加的难点是我需要使用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; } } 

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

我像这样创建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()上出现绑定错误,说明该地址已被使用。

如果我完全跳过使用Socket Factories:

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

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

问题:如何使用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系统属性 ,我想在像我这样的场景中经常会出现这种情况。

这应该有效:你的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的开头添加一个快捷方式,如果这= =那就返回true。

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