简体   繁体   English

RMI和JMX Socket工厂

[英]RMI and JMX Socket Factories

I'm trying to start an embedded JMX server in my java app. 我正在尝试在我的Java应用程序中启动嵌入式JMX服务器。 I want to use the same port for the RMI Registry and for the actual RMI traffic (or JMX traffic if you like). 我想为RMI注册表和实际的RMI流量使用相同的端口(如果你愿意,可以使用JMX流量)。 Apparently this is possible since the RMI Registry is merely a Remote Object itself . 显然这是可能的,因为RMI注册表本身只是一个远程对象

The added difficulty is that I need to use Socket Factories because I need to bind to a specific NIC. 增加的难点是我需要使用Socket Factories,因为我需要绑定到特定的NIC。 I start off by: 我从以下开始:

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

and here's my server socket factory. 这是我的服务器套接字工厂。 Pretty straight-forward stuff: 相当直接的东西:

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;
    }    
}

(the equals() and hashCode() are auto-generated by my IDE, don't get stuck on them) (我的IDE自动生成equals()hashCode() ,不要卡在它们上面)

I create the RMI Registry like this: 我像这样创建RMI注册表:

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

and then on to creating the JMXConnectorServer: 然后再创建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();

This results in a bind error on the connector.start() saying that the address is already in use. 这会导致connector.start()上出现绑定错误,说明该地址已被使用。

If I skip using Socket Factories altogether: 如果我完全跳过使用Socket Factories:

LocateRegistry.createRegistry(registryPort);

and

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


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

connector.start();

it works as expected, ie only a single port will be opened and no errors. 它按预期工作,即只打开一个端口,没有错误。

Question: How to make the 'single-listening-port-scenario' work with Socket Factories ? 问题:如何使用Socket Factories实现“单一侦听端口方案”?

UPDATE - FINAL SOLUTION 更新 - 最终解决方案

It works if you create the Registry with a null client socket factory, ie 如果您使用空客户端套接字工厂创建注册表,它可以工作

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

I also had to set the java.rmi.server.hostname System Property which I guess will often be the case in a scenario like mine. 我还必须设置java.rmi.server.hostname系统属性 ,我想在像我这样的场景中经常会出现这种情况。

This should work: you have a correct-looking equals() method in your ServerSocketFactory, which is the important bit. 这应该工作:你的ServerSocketFactory中有一个看起来正确的equals()方法,这是重要的一点。 RMI does call that. RMI确实称之为。 However the same doesn't apply currently to your client socket factory. 但是,目前不适用于您的客户端套接字工厂。 You need to pass null as the client socket factory, not RMISocketFactory.getDefaultSocketFactory(), as that gives you a sun.rmi.transport.proxy.RMIMasterSocketFactory, which doesn't implement equals() for some reason. 您需要传递null作为客户端套接字工厂,而不是RMISocketFactory.getDefaultSocketFactory(),因为它会为您提供sun.rmi.transport.proxy.RMIMasterSocketFactory,由于某种原因它不实现equals() Or else your own implementation of RMIClientSocketFactory with a plausible equals() method. 或者使用合理的equals()方法自己实现RMIClientSocketFactory

So what is happening here is that RMI is comparing the CSFs first, and they are coming out unequal, so it doesn't even bother comparing the SSFs: 所以这里发生的事情是RMI首先比较CSF,并且它们出现不平等,所以它甚至不打扰比较SSF:

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

so it tries to create a new ServerSocket on the port you specifed, which is the same as the first port, so it fails. 所以它试图在你指定的端口上创建一个新的ServerSocket ,它与第一个端口相同,所以它失败了。

You could add a shortcut at the beginning of equals that returns true if this == that. 你可以在equals的开头添加一个快捷方式,如果这= =那就返回true。

You should search for the JMXMP protocol, and for the jmxremote_optional.jar that contains it. 您应该搜索JMXMP协议以及包含它的jmxremote_optional.jar。 This is a more controllable, more efficient protocol for JMX. 这是一种更可控,更有效的JMX协议。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM