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协议。