Статьи

Экспонирование POJO как JMX MBean легко с помощью Spring

JMX — отличный способ проверить или изменить переменные состояния или вызвать метод в (удаленном) запущенном приложении через графический интерфейс управления, такой как JConsole. А Spring делает тривиальным представление любого POJO в виде JMX MBean с минимальной конфигурацией за несколько минут. Документация Spring JMX очень хороша, однако есть несколько моментов, с которыми я боролся некоторое время и поэтому хотел бы записать здесь правильные решения.

Мне нужно было отслеживать Java-приложение командной строки, используя Spring 2.5 в IBM JVM

1.4
1.5, работающий на сервере с jconsole в Sun JVM 1.6 в качестве клиента JMX на моем ПК.

Все фрагменты 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>

Однако есть также некоторые уловы, которые вы должны избегать:

  1. Вы должны запустить реестр RMI, чтобы соединитель мог зарегистрировать MBean там; это не начнется для вас
  2. Необходимо убедиться, что реестр запущен до того, как соединитель попытается использовать, либо объявив его перед соединителем, либо сделав эту зависимость явной с помощью атрибута зависимости от

Если вы не настроите его правильно, вы получите исключение, например

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 .

Связанные посты и документы:

Соединение с 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/