我正在尝试在我的 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/