AFAIK,
Вот возможные варианты подключения клиентского процесса JMX ( управляющего приложения , такого как jconsole, jmxterm, mc4j, jvmstat, jmxmonitor, jps, ...) к Процесс сервера JMX ( агент ).
Предполагается, что протокол, соединяющий JMX-клиента и JMX-сервер, - «Java RMI» (он же «RMI-JRMP»). Это должно быть по умолчанию. Можно настроить другие протоколы , в частности «RMI-IIOP» и «JMXMP». Возможны специальные протоколы: например, проект MX4J дополнительно предоставляет SOAP / HTTP и различные протоколы сериализации по HTTP.
Подробнее о конфигурации см. Документы Sun / Oracle .
Также взгляните на файл jre/lib/management/management.properties
в вашем дистрибутиве JDK.
Итак, возможности:
Случай 0: JVM запускается без какой-либо конкретной конфигурации
До Java 6: JVM не работает как сервер JMX. Любая программа, запускаемая внутри JVM, может получать программный доступ к MBeanServer JVM и использовать его для обмена интересными данными между потоками или для мониторинга JVM, но управление извне процесса JVM невозможно.
Начиная с Java 6: даже если явно не сконфигурировано, можно получить доступ к функциональности JMX JVM локально (с той же машины), как описано в «Случае 1».
Случай 1: JVM запускается с -Dcom.sun.management.jmxremote
JVM настроен для работы в качестве локального (только для одного компьютера) сервера JMX.
В этом случае (и в принципе только для JVM Sun / Oracle) клиент JMX может подключаться к серверу JMX через файлы сопоставления памяти, найденные в /tmp/hsperfdata_[user]
. Это упоминается в документации Sun и называется «локальный мониторинг» (а также Attach API ). Он не работает на файловых системах FAT, так как разрешения не могут быть правильно установлены там. См. эту запись в блоге .
Sun рекомендует запускать jconsole
на компьютере, отдельном от JMX-сервера, поскольку jconsole
, по-видимому, является источником ресурсов, поэтому эта вещь с "локальным мониторингом" не обязательно является хорошей идеей.
Локальный мониторинг, однако, довольно безопасен, его можно использовать только локально и легко контролировать с помощью разрешений файловой системы.
Случай 2: Сервер JMX запускается с -Dcom.sun.management.jmxremote.port=[rmiregistryport]
JVM настроен для работы в качестве сервера JMX, прослушивающего несколько портов TCP.
Порт, указанный в командной строке, будет выделен JVM, и там будет доступен реестр RMI. Реестр объявляет соединитель с именем «jmxrmi». Он указывает на второй случайным образом назначенный порт TCP («эфемерный» порт), через который сервер JMX RMI прослушивает и через который происходит фактический обмен данными.
Local, как описано в «Case 1», всегда включен в «Case 2».
Сервер JMX прослушивает все интерфейсы по умолчанию, поэтому вы можете подключиться к нему (и управлять им), локально подключившись к 127.0.0.1:[rmiregistryport], а также удаленно подключившись к [любому внешнему IP-адресу]: [some порт] удаленно.
Это означает, что вы должны посмотреть на последствия для безопасности . Вы можете заставить JVM прослушивать 127.0.0.1:[rmiregistryport] только установив -Dcom.sun.management.jmxremote.local.only=true
.
Весьма прискорбно, что нельзя указать, где будет располагаться эфемерный порт - он всегда выбирается случайным образом при запуске. Это может означать, что ваш брандмауэр должен стать швейцарским сыром проклятых! Однако есть обходных путей . В частности, Apache Tomcat устанавливает эфемерный порт сервера JMX RMI через свой JMX Remote Lifecycle Listener . Код для выполнения этой маленькой магии можно найти по адресу org.apache.catalina.mbeans.JmxRemoteLifecycleListener .
Если вы используете этот метод, вы также можете убедиться, что:
- Клиент JMX должен пройти аутентификацию на сервере JMX
- Обмен TCP между клиентом и сервером зашифрован с использованием SSL
Как это сделать, описано в документации Sun / Oracle
Другие подходы
Вы можете делать интересные перестановки, чтобы избежать необходимости использовать протокол RMI. В частности, вы можете добавить в свой процесс движок сервлетов (например, Jetty). Затем добавьте сервлеты, которые преобразуют внутренний обмен на основе HTTP в прямой доступ к JVM MBeanServer
. Тогда вы окажетесь в «случае 0», но все еще будете иметь возможности управления, возможно, через интерфейс на основе HTML. JBoss JMX Console является примером этого.
Больше не по теме, вы можете использовать SNMP напрямую (что я не пробовал) в соответствии с этим документом .
Показать и сказать время
А теперь пришло время для некоторого кода проиллюстрировать обмен JXM. Мы черпаем вдохновение из учебника Sunoracle .
Это работает в Unix. Мы используем JVM, настроенную в качестве сервера JMX, используя:
-Dcom.sun.management.jmxremote.port=9001
Мы используем lsof
, чтобы проверить, какие порты TCP открыты:
lsof -p <processid> -n | grep TCP
Видно что-то вроде этого, порт реестра и эфемерный порт:
java 1068 user 127u IPv6 125614246 TCP *:36828 (LISTEN)
java 1068 user 130u IPv6 125614248 TCP *:9001 (LISTEN)
Мы используем tcpdump
для проверки обмена пакетами между клиентом JMX и сервером JMX:
tcpdump -l -XX port 36828 or port 9001
Мы установили файл .java.policy
в домашнем каталоге, чтобы позволить клиенту фактически подключаться удаленно:
grant {
permission java.net.SocketPermission
"<JMX server IP address>:1024-65535", "connect,resolve";
};
И тогда мы можем запустить это и посмотреть, что произойдет:
package rmi;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import javax.management.remote.rmi.RMIConnection;
import javax.management.remote.rmi.RMIServer;
public class Rmi {
public static void main(String args[]) throws Exception {
// We need a Security Manager (not necessarily an RMISecurityManager)
if (System.getSecurityManager() == null) {
System.setSecurityManager(new SecurityManager());
}
//
// Define a registry (this is just about building a local data structure)
//
final int comSunManagementJmxRemotePort = 9001;
Registry registry = LocateRegistry.getRegistry("<JMX server IP address>", comSunManagementJmxRemotePort);
//
// List registry entries. The client connects (using TCP) to the server on the
// 'com.sun.management.jmxremote.port' and queries data to fill the local registry structure.
// Among others, a definition for 'jmxrmi' is obtained.
//
System.out.print("Press enter to list registry entries");
System.in.read();
String[] names = registry.list();
for (String name : names) {
System.out.println("In the registry: " + name);
}
//
// 'Looking up' the entry registered under 'jmxrmi' involves opening and tearing down
// a TCP connection to the 'com.sun.management.jmxremote.port', as well as a TCP
// connection to an ephemeral secondary port chosen at server startup.
// The actual object locally obtained is a "javax.management.remote.rmi.RMIServerImpl_Stub"
// indicating where the ephemeral port is.
// "RMIServerImpl_Stub[UnicastRef [liveRef: [endpoint:[$IP:$EPHEMERAL_PORT](remote),objID:[-62fb4c1c:131a8c709f4:-7fff, -3335792051140327600]]]]"
//
System.out.print("Press enter to get the 'jmxrmi' stub");
System.in.read();
RMIServer jmxrmiServer = (RMIServer)registry.lookup("jmxrmi");
System.out.println(jmxrmiServer.toString());
//
// Now get a "RMI Connection" to the remote. This involves setting up and tearing
// down a TCP connection to the ephemeral port.
//
System.out.print("Press enter to get the 'RMIConnection'");
System.in.read();
RMIConnection rcon = jmxrmiServer.newClient(null);
//
// Ask away. This involves setting up and tearing
// down a TCP connection to the ephemeral port.
//
System.out.print("Press enter to get the 'domains'");
System.in.read();
for (String domain : rcon.getDomains(null)) {
System.out.println("Domain: " + domain);
}
//
// Ok, that will do. For serious applications, we better use the higher-level JMX classes
//
}
}