允许在立即启用 JMX 监控的情况下重新启动 Java 应用程序

Posted

技术标签:

【中文标题】允许在立即启用 JMX 监控的情况下重新启动 Java 应用程序【英文标题】:Allow restarting Java application with JMX monitoring enabled immediately 【发布时间】: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 的问题(如***.com/q/3229860/602119),其中也提到了 TIME_WAIT 在答案中。 【参考方案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

以上是关于允许在立即启用 JMX 监控的情况下重新启动 Java 应用程序的主要内容,如果未能解决你的问题,请参考以下文章

激活 WDAC 规则 16 不允许在不重新启动的情况下应用策略

Wildfly 17 在同一服务器上启用 JMX 远程会导致 logmanager 错误

Zabbix监控记录

JMX远程监控JVM

使用Collectd + InfluxDB + Grafana进行JMX监控

如何通过JMX远程监控Solr?