【问题标题】:Allow restarting Java application with JMX monitoring enabled immediately允许在立即启用 JMX 监控的情况下重新启动 Java 应用程序
【发布时间】:2014-06-25 05:49:14
【问题描述】:

我有一个启用了 JMX 监控的 Java 应用程序,如下所示:

-Dcom.sun.management.jmxremote.port=9999 \
// some other properties omitted

但是当我尝试重新启动应用程序时,有时我收到一条错误消息,提示 JMX 端口号已在使用中。这是不可接受的。

所以我想将底层套接字的 SO_REUSEADDR 设置为 true 以避免此错误,但没有找到相关的 JMX 属性。

有什么想法吗?

【问题讨论】:

  • 您是否查看过哪个应用程序正在使用该端口?
  • 我必须是我的应用程序。当我停止应用程序时,我认为绑定到此端口的套接字进入 TIME_WAIT 状态,以便 2MSL 实际关闭。所以我想让这个端口可重用。
  • SO_REUSEADDR 不能那样工作。它允许套接字侦听特定的 IP 地址并忽略其他地址。同一个应用程序运行了两次,或者有另一个应用程序占用了这个端口。
  • @BevynQ,如果我停止应用程序并且不立即启动它,然后我尝试启动应用程序,我可以工作。所以我认为这是套接字TIME_WAIT机制不允许
  • @NeilCoffey netstat 如果进程已经完成,则不会为“TIME_WAIT”命名进程,例如tcp 0 0 localhost:57525 localhost:3100 TIME_WAIT -。通常还有一些关于 SO_REUSEADDR 的问题(如stackoverflow.com/q/3229860/602119),其中也提到了 TIME_WAIT 在答案中。

标签: java jmx restart shutdown reusability


【解决方案1】:

恐怕你不能从命令行做到这一点。

您需要创建一个RMIServerSocketFactory,它会生成带有所需选项的ServerSockets (SO_REUSEADDR)。

这里的文档:http://docs.oracle.com/javase/8/docs/technotes/guides/rmi/socketfactory/

其他人解决了同样的问题: https://svn.apache.org/viewvc?view=revision&revision=r1596579

【讨论】:

  • 谢谢。恐怕这确实是唯一的解决方案。把它们放在一起比我希望的要冗长得多,但总比没有好。
【解决方案2】:

我有同样的问题。这是我的应用程序的第一个实例(我已经停止了),它仍然订阅了这个端口,所以新实例无法启动。在我的情况下,它与套接字 TIME_WAIT 机制无关,而是与调用stop() 后,所有正在运行的线程正常结束需要一些时间有关。在我的案例中起作用的是在停止应用程序之前取消注册 bean,以便套接字空闲。

private void unregisterBeanForName(String name) {
        try {

            JMXServiceURL jmxServiceURL = new JMXServiceURL("service:jmx:rmi:///jndi/rmi://127.0.0.1:9999/jmxrmi");
            JMXConnector cc = JMXConnectorFactory.connect(jmxServiceURL);
            MBeanServerConnection mbsc = cc.getMBeanServerConnection();
//This information is available in jconsole
            ObjectName serviceConfigName = new ObjectName("YourObjectName");
            mbsc.unregisterMBean(serviceConfigName);
// Close JMX connector
            cc.close();
        } catch (Exception e) {
            System.out.println("Exception occurred: " + e.toString());
            e.printStackTrace();
        }
    }

【讨论】:

    【解决方案3】:

    是的,您应该以编程方式创建 JMX 连接器。 作为更简单的解决方法,如果默认端口因刚刚杀死的进程而忙碌,您可以在运行时为 JMX 选择另一个端口。或者只是尝试一次又一次地打开您的端口,直到它成功。

    这是我用来打开与 JConsole 兼容的 JMX 连接器的代码 sn-p。在 Scala 中,抱歉,但您应该能够轻松适应它

    def startJmx(port: Int): Unit = {
    if (port < 1) {
      return
    }
    
    val log = LoggerFactory.getLogger(getClass)
    
    log.info("Starting JMX server connector on port {}", port)
    
    val registry = LocateRegistry.createRegistry(port)
    
    val server = ManagementFactory.getPlatformMBeanServer()
    
    val url = new JMXServiceURL(s"service:jmx:rmi:///jndi/rmi://localhost:$port/jmxrmi")
    
    val connectorServer = JMXConnectorServerFactory.newJMXConnectorServer(url, Collections.emptyMap(), server)
    
     val thread = new Thread {
       override def run = try {
         connectorServer.start()
       } catch { 
         case e: Exception => log.error("Unable to start JMX connector", e)
       }
     }
     thread.setDaemon(true)
     thread.setName("JMX connector Thread")
     thread.start()
    }
    

    【讨论】:

      【解决方案4】:

      这可能是一种解决方法: 在远程服务器上,您可以有两个端口:9999 和 9998 转发到 9999。

      在重新启动您的应用程序时,每次交替使用一个布尔值来决定连接到 9999 还是 9998。

      【讨论】:

        【解决方案5】:

        在您的应用程序上添加一个关闭挂钩,它将杀死 jmx。

        // kill process with port 9999    
        fuser -k 9999/tcp
        

        【讨论】:

        • 这将取决于平台(在我的情况下可以),但更重要的是它不起作用。通过 fuser 终止进程似乎不会改变关于 SO_REUSEADDR 的行为。我尝试了一个简单的类,但仍然得到了 BindException
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-08-13
        • 1970-01-01
        • 2014-04-18
        • 2018-02-09
        • 1970-01-01
        相关资源
        最近更新 更多