允许在立即启用 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 错误