Разрешить перезапуск приложения Java с включенным мониторингом JMX
У меня есть приложение Java с мониторингом JMX, например:
-Dcom.sun.management.jmxremote.port=9999 \
// some other properties omitted
Но когда я пытаюсь перезапустить приложение, когда-то я получил сообщение об ошибке, говорит, что номер порта JMX уже используется. Это неприемлемо.
Итак, я хочу установить SO_REUSEADDR в true для базового сокета, чтобы избежать этой ошибки, но не обнаружил никаких связанных свойств JMX.
Любая идея?
Ответы
Ответ 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
Да, вы должны программно создать коннектор JMX.
В качестве упрощенного решения вы можете выбрать другой порт для JMX во время выполнения, если порт по умолчанию занят только что убитым процессом. ИЛИ просто пытайтесь открыть порт снова и снова, пока не добьется успеха.
Вот фрагмент кода, который я использую для открытия JConsole-совместимого JMX-коннектора. В 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()
}
Ответ 3
У меня была такая же проблема. Это был первый экземпляр моего приложения (которое я остановил), которое все еще подписывалось на этот порт, поэтому новый экземпляр не мог запускаться. В моем случае это не было связано с механизмом 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();
}
}
Ответ 4
Это может быть одно решение:
На удаленном сервере вы можете иметь два порта: 9999 и 9998, перенаправленные на 9999.
При повторном запуске приложения каждый раз изменяйте логическое значение, чтобы решить подключиться к 9999 или 9998.
Ответ 5
Добавьте к вашему приложению заглушку завершения, которая убьет jmx.
// kill process with port 9999
fuser -k 9999/tcp