Открывает ли Java 6 порт по умолчанию для удаленных подключений JMX? - PullRequest
28 голосов
/ 05 февраля 2009

Мой конкретный вопрос связан с JMX, как в JDK 1.6: если я запускаю процесс Java с использованием JRE 1.6 с

com.sun.management.jmxremote

в командной строке: выбирает ли Java порт по умолчанию для удаленных JMX-соединений?

Предыстория: в настоящее время я пытаюсь разработать процедуру, чтобы предоставить клиентам, которые позволят им подключаться к одному из наших процессов через JMX с удаленного компьютера. Цель состоит в том, чтобы облегчить их удаленную отладку ситуации, происходящей на консоли дисплея реального времени. Из-за соглашения об уровне обслуживания у них есть сильная мотивация собирать как можно больше данных и, если ситуация выглядит слишком сложной, чтобы ее можно было быстро исправить, перезапустить консоль дисплея и позволить ей повторно подключиться к стороне сервера.

Мне известно, что я могу запустить jconsole на процессах JDK 1.6 и jvisualvm на процессах после JDK 1.6.7 с физическим доступом к консоли. Однако из-за эксплуатационных требований и проблем с персоналом у нас есть сильная мотивация для удаленного сбора данных, которые нам нужны, и их повторной настройки и запуска.

РЕДАКТИРОВАТЬ: я знаю о свойстве порта командной строки

com.sun.management.jmxremote.port=portNum

Вопрос, на который я пытаюсь ответить, заключается в том, что, если вы не установите это свойство в командной строке, выберет ли Java другой порт для удаленного мониторинга? Если да, то как вы можете определить, что это может быть?

Ответы [ 7 ]

87 голосов
/ 08 августа 2011

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 .

Если вы используете этот метод, вы также можете убедиться, что:

  1. Клиент JMX должен пройти аутентификацию на сервере JMX
  2. Обмен 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
        //
    }   
}
38 голосов
/ 05 февраля 2009

Документация предполагает, что агент JMX использует локальный порт - что-то недоступное извне машины - если не указано следующее свойство:

com.sun.management.jmxremote.port=portNum

Это по соображениям безопасности, а также по причинам, указанным г-ном Картофелем Хедом. Таким образом, похоже, что Java 6 не открывает стандартный доступный удаленно доступный порт для JMX.

РЕДАКТИРОВАТЬ: Добавлено после ОП добавил ответ с дополнительной информацией.

Другой вариант - создать локальный прокси-сервер, который прослушивает все локальные соединения JMX и экспортирует эту информацию. Таким образом, вам не нужно иметь такую ​​волшебную конфигурацию каждого экземпляра JVM на сервере. Вместо этого локальный прокси-сервер может подключаться ко всем JVM через JMX, а затем каким-либо образом предоставлять эту информацию удаленно. Я не уверен, как именно вы бы это реализовали, но что-то вроде этого может оказаться менее трудоемким, чем то, что вы в противном случае должны были бы сделать для раскрытия всех ваших JVM удаленно через JMX.

6 голосов
/ 25 ноября 2011

На самом деле существует недокументированное свойство, которое вы можете использовать, чтобы заставить JMX создавать удаленно доступные соединители на случайных номерах портов.

-Dcom.sun.management.jmxremote.authenticate="false" 
-Dcom.sun.management.jmxremote="true" 
-Dcom.sun.management.jmxremote.ssl="false" 
-Dcom.sun.management.jmxremote.port="0"
-Dcom.sun.management.jmxremote.local.only="false"

Последние два свойства имеют наибольшее значение.

3 голосов
/ 05 февраля 2009

Документация , кажется, указывает, что агент JMX использует локальный временный порт, , если не указано следующее свойство:

com.sun.management.jmxremote.port=portNum

Использование портов по умолчанию исключено, потому что в одной системе может быть много java-приложений, а если бы был порт по умолчанию, управлять было бы только одно приложение! Вышеуказанное свойство конфигурации предоставляется для экспресс-назначения из удаленного управления.

Если вы должны настаивать на использовании эфемерного порта, то URL-адрес агента JMX должен быть доступен из JVM через следующее системное свойство (хотя это, вероятно, будет локальный адрес):

com.sun.management.jmxremote.localConnectorAddress

Примечание : Полагаю, вы всегда можете открыть сокет по удаленно доступному адресу и прокси-запросы к локальному сокету, но использование доступной опции кажется гораздо более привлекательным!

2 голосов
/ 23 июля 2012

Недавно я работал над тем, чтобы выяснить, как включить удаленное управление JMX из кода Java, не требуя запуска JVM со специальными свойствами. Решение, на котором я остановился, состоит в том, чтобы запустить собственный личный реестр RMI - достаточно легко - и открыть для него службу JMX. Я создаю свой собственный MBeanServer, затем создаю новый JMXConnectorServer. JMXConnectorServer создается с помощью вызова, подобного

connector = JXMConnectorServerFactory.newJMXConnectorServer(url, null, server);

Где сервер - это MBeanServer, а url - это экземпляр JMXServiceURL.

URL-адрес имеет форму "service: jmx: rmi: /// jndi / rmi: // localhost: / jmxrmi", где port - это номер порта (локального) частного реестра. "jmxrmi" - это стандартное имя службы для службы JMX.

После настройки и запуска коннектора я обнаружил, что могу подключиться к нему из jconsole, используя имя хоста: порт.

Это полностью отвечает моим потребностям; Мне будет интересно узнать, видит ли кто-нибудь недостаток в этом подходе.

Ссылка: Учебное пособие по JMX, гл. 3

2 голосов
/ 10 февраля 2009

Итак, короткий ответ на мой вопрос "нет".

Однако интересно выяснить почему. Посмотрите на вывод netstat действительного локального соединения. Вот порты, которые я вижу открытыми в результате jconsole локального подключения к себе. Как видите, порт 1650 - это локальный порт, используемый для информации JMX:

Proto  Local Address          Foreign Address        State
TCP    Gandalf:1650           Gandalf:1652           ESTABLISHED
TCP    Gandalf:1650           Gandalf:1653           ESTABLISHED
TCP    Gandalf:1650           Gandalf:1654           ESTABLISHED
TCP    Gandalf:1650           Gandalf:1655           ESTABLISHED
TCP    Gandalf:1650           Gandalf:1656           ESTABLISHED
TCP    Gandalf:1652           Gandalf:1650           ESTABLISHED
TCP    Gandalf:1653           Gandalf:1650           ESTABLISHED
TCP    Gandalf:1654           Gandalf:1650           ESTABLISHED
TCP    Gandalf:1655           Gandalf:1650           ESTABLISHED
TCP    Gandalf:1656           Gandalf:1650           ESTABLISHED

Однако недостаточно попытаться подключить jconsole к localhost:1650. К сожалению, все, что вас заинтересует, это сообщение «Соединение не удалось: в таблице нет такого объекта».

Итак, вывод из моей оригинальной истории заключается в том, что, если мы собираемся упростить удаленный мониторинг с помощью JMX для наших клиентов, нам действительно необходимо определить уникальные индивидуальные порты удаленного доступа для различных процессов Java, которые запускаются в нашей системе. , К счастью, все это требует разумного использования аргумента VM:

com.sun.management.jmxremote.port=portNum

, где мы почти наверняка будем иметь последовательный предварительно заданный диапазон portNum, чтобы клиент мог выбрать правильное удаленное приложение, используя номер порта.

0 голосов
/ 21 марта 2015

Если вы запустили свое приложение на сервере приложений Glassfish, просто запустите следующую команду asadmin, вам нужно будет перезапустить все работающие серверы, чтобы изменения вступили в силу.

. / Asadmin enable-secure-admin

Существуют дополнительные конфигурации сервера Glassfish для дальнейшего обеспечения безопасности, подробнее см. Удаленное подключение к Glassfish через JMX .

...