JMX — отличный способ проверить или изменить переменные состояния или вызвать метод в (удаленном) запущенном приложении через графический интерфейс управления, такой как JConsole. А Spring делает тривиальным представление любого POJO в виде JMX MBean с минимальной конфигурацией за несколько минут. Документация Spring JMX очень хороша, однако есть несколько моментов, с которыми я боролся некоторое время и поэтому хотел бы записать здесь правильные решения.
Мне нужно было отслеживать Java-приложение командной строки, используя Spring 2.5 в IBM JVM
1.5, работающий на сервере с jconsole в Sun JVM 1.6 в качестве клиента JMX на моем ПК.
1.4
Все фрагменты XML взяты из Spring application-context.xml. Если вы раньше не использовали Spring, прочтите руководство по его настройке и внедрению зависимостей.
Превращение POJO в MBean
JMX позволяет выставлять методы получения, установки и операции, принимающие примитивные или сложные типы данных в качестве параметров (хотя типы, отличные от немногих специальных, требуют, чтобы у клиента были классы). Вы говорите Spring, чтобы выставить POJO как MBean следующим образом:
<bean id="myMBean" class="my.package.JobPerformanceStats" factory-method="instance" /> <bean class="org.springframework.jmx.export.MBeanExporter" lazy-init="false"> <property name="beans"> <map> <entry key="bean:name=MyMBeanName" value-ref="myMBean"/> </map> </property> </bean>
Сначала вы объявляете экземпляр класса POJO — myMBean (по другим причинам у меня старомодный синглтон и для доступа к компоненту используется JobPerformanceStats.instance ()). Затем вы объявляете MBeanExporter с помощью lazy-init = ”false” и рассказываете о своем бине. (Существуют также другие способы сделать это, включая автообнаружение.) Затем компонент будет виден под его ключом, т.е. «bean: name = MyMBeanName», который JConsole отображает как «MyMBeanName».
Обратите внимание, что MBeanExporter работает только в JVM 1.5+, поскольку использует новый пакет java.lang.management. Под 1.4 весна провалится с
java.lang.NoClassDefFoundError: javax/management/MBeanServerFactory at org.springframework.jmx.support.MBeanServerFactoryBean.createMBeanServer
По умолчанию он предоставляет все открытые методы и атрибуты. Вы можете изменить это различными способами, например, с помощью интерфейса.
Если вы не работаете в контейнере, который уже предоставляет сервер MBean, как в моем случае здесь, вы должны указать Spring, чтобы запустить его:
<bean class="org.springframework.jmx.support.MBeanServerFactoryBean"/>
Включение удаленного доступа
Чтобы сделать MBean доступным с другого компьютера, вы должны представить его миру, объявив ConnectorServerFactoryBean, настроенный с соответствующим механизмом связи.
Удаленный доступ через JMXMP
По умолчанию ConnectorServerFactoryBean предоставляет MBeans поверх JMXMP с адресной службой: jmx: jmxmp: // localhost: 9875 :
<bean class="org.springframework.jmx.support.ConnectorServerFactoryBean" />
Однако этот протокол не поддерживается «из коробки», и вы должны включить jmxremote_optional.jar, часть OpenDMK , в путь к классу как приложения MBean, так и клиента jconsole, чтобы избежать исключения
org.springframework.beans.factory.BeanCreationException: Ошибка создания компонента с именем org.springframework.jmx.support.ConnectorServerFactoryBean # 0 ′, определенного в ресурсе пути к классу [application-context.xml]: сбой вызова метода init; вложенным исключением является java.net.MalformedURLException: неподдерживаемый протокол: jmxmp
Удаленный доступ через RMI
В качестве альтернативы вы можете выставить MBeans поверх RMI, который не имеет дополнительных зависимостей:
<!-- Now expose the server for remote access via RMI Local access: service:jmx:rmi://localhost/jndi/rmi://localhost:10099/myconnector Remote access: service:jmx:rmi://your.server.com/jndi/rmi://localhost:10099/myconnector --> <bean class="org.springframework.jmx.support.ConnectorServerFactoryBean" depends-on="rmiRegistry"> <property name="objectName" value="connector:name=rmi" /> <property name="serviceUrl" value="service:jmx:rmi://localhost/jndi/rmi://localhost:10099/myconnector" /> </bean> <bean id="rmiRegistry" class="org.springframework.remoting.rmi.RmiRegistryFactoryBean"> <property name="port" value="10099" /> </bean>
Однако есть также некоторые уловы, которые вы должны избегать:
- Вы должны запустить реестр RMI, чтобы соединитель мог зарегистрировать MBean там; это не начнется для вас
- Необходимо убедиться, что реестр запущен до того, как соединитель попытается использовать, либо объявив его перед соединителем, либо сделав эту зависимость явной с помощью атрибута зависимости от
Если вы не настроите его правильно, вы получите исключение, например
org.springframework.beans.factory.BeanCreationException: Ошибка создания компонента с именем org.springframework.jmx.support.ConnectorServerFactoryBean # 0 ′, определенного в ресурсе пути к классу [application-context.xml]: сбой вызова метода init; вложенное исключение — java.io.IOException: невозможно связать с URL [rmi: // localhost: 10099 / jmxrmi]: javax.naming.ServiceUnavailableException [Исключением корня является java.rmi.ConnectException: соединение с хостом отказано: localhost; Вложенное исключение: java.net.ConnectException: соединение отклонено: соединение ].
Доступ к локальному серверу MBean через туннель SSH
Для повышения безопасности вы можете предпочесть не предоставлять свои MBeans удаленному доступу, сделав их доступными только с локального компьютера (127.0.0.1), и использовать туннель SSH, чтобы удаленная JConsole могла обращаться к ним как к локальному приложению. Это, конечно, возможно, но может быть трудным, потому что обычно JMX проходит через RMI, который использует два порта : один для Реестра RMI и другой для фактической службы (здесь сервер MBean), который обычно выбирается случайным образом во время выполнения, и вы ‘ Мне нужно туннелировать оба. К счастью, Spring позволяет настроить оба порта :
<bean class="org.springframework.jmx.support.ConnectorServerFactoryBean" depends-on="rmiRegistry"> <property name="objectName" value="connector:name=rmi" /> <property name="serviceUrl" value="service:jmx:rmi://127.0.0.1:STUBPORT/jndi/rmi://localhost:REGISTRYPORT/myconnector" /> </bean> <bean id="rmiRegistry" class="org.springframework.remoting.rmi.RmiRegistryFactoryBean"> <property name="port" value="REGISTRYPORT" /> </bean>
Замените STUBPORT и REGISTRYPORT подходящими номерами и туннелируйте эти два. Обратите внимание, что номер REGISTRYPORT совпадает в serviceUrl соединителя и в атрибуте порта реестра RMI.
ПРЕДУПРЕЖДЕНИЕ . Приведенная выше конфигурация фактически не препятствует прямому доступу из удаленного приложения. Чтобы действительно заставить реестр RMI только прослушивать соединение с локального хоста, нам, вероятно, потребуется установить — под Sun JVM без Spring — системное свойство com.sun.management.jmxremote. Кроме того, чтобы заставить реестр использовать IP 120.0.0.1, нам нужно установить java.rmi.server.hostname = localhost (также относится и к Spring). Смотрите это обсуждение о принудительном локальном доступе . Я не уверен, как добиться того же результата с Spring, сохраняя при этом возможность указать оба порта RMI. Проверьте также JavaDoc для Spring RmiServiceExporter .
Связанные посты и документы:
- Отладка туннелирования и JMX для Alfresco (А. использует Spring) — см. Второй раздел, Туннелирование SSH для JMX
- Пользовательский туннельный агент RMI — использует настроенный порт вместо случайного
- Мониторинг ActiveMQ с использованием JMX через SSH
- JMX 1.2 спецификация и JMX 1.0 Удаленные спецификации API ; из спецификации JMX: «Сервер MBean использует адаптеры и соединители протокола , чтобы сделать агент
доступным из приложений управления вне JVM агента» . С другой стороны, страница Oracle JMX гласит, что если вы установите com.sun.management.jmxremote (в отличие от… jmxremote.port), вы сможете «контролировать локальную платформу Java, то есть JVM, работающую на та же самая машина »- таким образом, не обязательно от той же JVM.
Соединение с JConsole
Запустите JConsole и введите соответствующий удаленный адрес, например, service: jmx: rmi: /// jndi / rmi: //your.server.com: 10099 / myconnector, если вы подключаетесь к приложению на удаленной машине your.server. com доступен через RMI.
Что касается URL-адреса соединения, если у вас есть соединитель с serviceUrl «service: jmx: rmi: // myhost: 9999 / jndi / rmi: // localhost: 10099 / myconnector», то с клиента вы можете использовать любой сервис : jmx: rmi: // myhost: 9999 / jndi / rmi: //your.server.com: 10099 / myconnector или просто служба: jmx: rmi: /// jndi / rmi: //your.server.com: 10099 / myconnector, поскольку в соответствии со спецификацией JMX 1.2 Remote API (стр. 90):
… имя хоста и номер порта
# (myhost: 9999 в примерах) не используются клиентом и, если
# присутствует, по сути являются комментариями. Адрес сервера соединителя
# фактически сохраняется в сериализованной заглушке (/ stub / form) или в
записи каталога # (/ jndi / form).
Конфигурация IBM JVM, JConsole и JMX
В руководстве IBM JVM 5 SDK указано, что IBM SDK также содержит JConsole и распознает те же системные свойства , связанные с JMX , а именно com.sun.management.jmxremote. * (Хотя сам «com.sun.management.jmxremote» не упоминается ).
Обратите внимание, что IBM JConsole немного отличается, например, отсутствует вкладка Локальная, которая заменяется указанием параметра командной строки connection = localhost (поищите в руководстве по SDK «Локальная вкладка инструмента мониторинга JConsole»).
Дальнейшие улучшения
JVM 1.5: экспонирование MemoryMXBean
Начиная с Java 5.0 существует несколько полезных платформ MBean, которые предоставляют информацию о JVM, включая также java.lang.management.MemoryMXBean , которые позволяют вам видеть использование кучи, вызывать GC и т. Д.
Вы можете сделать его доступным для JConsole и других агентов JMX следующим образом (хотя должен быть более простой способ):
<bean class="org.springframework.jmx.export.MBeanExporter" lazy-init="false"> <property name="beans"> <map> <entry key="bean:name=Memory2" value-ref="memProxy"/> <!-- other exported beans may follow ... --> </map> </property> </bean> <bean id="memProxy" class="java.lang.management.ManagementFactory" factory-method="getMemoryMXBean" />
Обновление : действительно, кажется, есть лучший способ выставить MBeans платформы напрямую , заменив MBeanServerFactoryBean в Spring на java.lang.management.ManagementFactory, используя фабричный метод getPlatformMBeanServer. Конечно, для этого требуется JVM 1.5+.
Повышение безопасности с помощью аутентификации по паролю
Доступ к вашим MBeans через RMI может быть защищен паролем. Согласно обсуждению, аутентификация настроена на соединителе сервера :
<bean class="org.springframework.jmx.support.ConnectorServerFactoryBean" depends-on="rmiRegistry"> <property name="objectName" value="connector:name=rmi" /> <property name="serviceUrl" value="service:jmx:rmi://localhost/jndi/rmi://localhost:10099/myconnector" /> <property name="environment"> <!-- the following is only valid when the sun jmx implementation is used --> <map> <entry key="jmx.remote.x.password.file" value="etc/security/jmxremote.password"/> <entry key="jmx.remote.x.access.file" value="etc/security/jmxremote.access"/> </map> </property> </bean>
Файлы passwd и access следуют шаблонам, которые можно найти в папке JDK / jre / lib / management.
Резюме
Экспонировать POJO как MBean с Spring очень просто, просто не забудьте запустить сервер MBean и коннектор. Для JMXMP включите имплемент jmxmp. jar на classpath и для RMI убедитесь, что запустили реестр RMI перед соединителем.
От http://theholyjava.wordpress.com/2010/09/16/exposing-a-pojo-as-a-jmx-mbean-easily-with-spring/