我与JMX监控Java应用程序启用这样的:允许与JMX监控重新启动Java应用程序启用立即
-Dcom.sun.management.jmxremote.port=9999 \
// some other properties omitted
但是,当我尝试重新启动应用程序,有时我得到一个错误的JMX端口号说已在使用中。这是不可接受的。
所以我想为底层套接字设置SO_REUSEADDR为true来避免这个错误,但是没有找到相关的JMX属性。
有什么想法?
我与JMX监控Java应用程序启用这样的:允许与JMX监控重新启动Java应用程序启用立即
-Dcom.sun.management.jmxremote.port=9999 \
// some other properties omitted
但是,当我尝试重新启动应用程序,有时我得到一个错误的JMX端口号说已在使用中。这是不可接受的。
所以我想为底层套接字设置SO_REUSEADDR为true来避免这个错误,但是没有找到相关的JMX属性。
有什么想法?
恐怕你不能从命令行那样做。
您将需要创建一个RMIServerSocketFactory
,其中生成ServerSockets
与所需的选项(SO_REUSEADDR
)。
文档浏览:http://docs.oracle.com/javase/8/docs/technotes/guides/rmi/socketfactory/
别人解决同样的问题: https://svn.apache.org/viewvc?view=revision&revision=r1596579
谢谢。恐怕这确实是唯一的解决方案。综合起来,它比我想象的要冗长得多,但仍然比没有更好。 – Boris
添加一个关闭挂钩上的应用程序,它会杀死JMX。
// kill process with port 9999
fuser -k 9999/tcp
这将取决于平台(在我的情况下这可以),但更重要的是它不起作用。通过fuser杀死进程似乎没有改变有关SO_REUSEADDR的行为。我尝试了一个简单的类,但仍然得到了BindException – Boris
是的,您应该以编程方式创建JMX连接器。 作为更简单的解决方法,如果默认端口正忙于死亡进程,则可以在运行时为JMX选择另一个端口。或者只是尝试一次又一次地打开您的端口,直到成功。
这里是我用来打开JConsole兼容的JMX连接器的代码片段。在斯卡拉,对不起,但你应该能够很容易地适应它
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()
}
我有同样的问题。这是我的应用程序的第一个实例(我停止了),它仍然订阅了此端口,所以新实例无法启动。在我的情况下,它不需要使用套接字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();
}
}
这可能是一个解决办法: 在远程服务器上,你可以有两个端口:9999和9998转〜9999
在每次重新启动你的应用程序替代一个布尔值来决定连接到9999或9998.
您是否看过使用该端口的应用程序? – BevynQ
我必须是我的应用程序。当我停止应用程序时,我认为绑定到这个端口的套接字实际上关闭了2MSL的TIME_WAIT状态。所以我想让这个端口可重用。 – George
SO_REUSEADDR不能这样工作。它允许套接字监听特定的IP地址并忽略其他地址。要么同一个应用程序运行两次,要么有另一个应用程序抓住这个端口。 – BevynQ