Использование компонентов управления платформой Java

Следите за вашими приложениями Java SE 5.0

В последней версии платформы Java™ имеется несколько новых функциональных возможностей для мониторинга и управления. В данной статье трое разработчиков группы IBM Java Technology Centre помогут вам начать использование этого API. После краткого обзора пакета java.lang.management они рассмотрят несколько коротких практических сценариев для испытания производительности работающей JVM.

Мэй Гловер Ганн, инженер-программист, IBM

Мэй Гловер Ганн (May Glover Gunn) является физиком по профессии, но по прихоти судьбы поступила в IBM три года назад. С того времени она работает в отделе Java Technology Centre компании IBM Hursley Laboratory на различных должностях. Недавно она разработала тесты по технологии Java 5.0 и в настоящее время работает с новыми технологиями, связанными с платформой Java.



Джордж Харли, разработчик программного обеспечения, IBM

Джордж Харли (George Harley) является старшим разработчиком IBM Hursley Laboratory отдела Java Technology Centre. Он более десятилетия профессионально занимается созданием программного обеспечения на различных языках программирования. В свое свободное время он работает.



Кэролин Гаф, инженер-программист, IBM

Кэролин Гаф (Caroline Gough) была программистом-разработчиком в маленькой фирме за три года до прихода в команду Java Technology Centre System Test компании IBM Hursley Laboratory. Сейчас она работает старшим тестировщиком, специализируясь на тестировании в предельных режимах и средствах RAS (reliability, availability, and serviceability - надежность, доступность и обслуживаемость). Она работала с последним IBM-портом платформы Java версии 5.0, а в настоящее время готовит тесты для следующих версий платформы Java.



11.04.2006

Среди многих новых возможностей, представленных в версии 5.0 платформы Java 2, имеется API, позволяющий Java-приложениям и совместимым программам выполнять мониторинг и управлять виртуальной машиной Java (JVM) и операционной системой, на которой она выполняется. В этой статье вы узнаете о возможностях этого нового API управления платформой, расположенного в пакете java.lang.management. Данная статья поможет вам ускорить использование нового набора мощных возможностей, которые станут еще более важными в следующих версиях платформы Java.

Мониторинг и управление VM 5.0

Java 5.0 предоставляет новые возможности для мониторинга и управления работающей виртуальной машиной. Разработчики и системные администраторы могут следить (а некоторые свойства точно регулировать) за производительностью VM 5.0. Механизм работы с этими возможностями будет знаком любому, кто имеет опыт использования Java Management Extensions (JMX). При помощи JMX-технологии набор контролируемых ресурсов платформы можно рассматривать как простые, четко определенные объекты, чьи свойства соответствуют низкоуровневым характеристикам конкретного ресурса.

В API управления платформой эти четко-определенные объекты называются компонентами MXBean. Если вы полагаете, что MXBean звучит как вид, возможно, более знакомого MBean, то вы правы. Эти компоненты MXBean (или компоненты MBean платформы) являются, в сущности, компонентами управления, охватывающими определенные части внутреннего устройства платформы 5.0. На рисунке 1 изображены компоненты MXBean в общем контексте.

Рисунок 1. Компоненты MXBean предоставляют управляющие интерфейсы для Java-платформы
MXBeans provide management interfaces for the Java platform

Вы можете найти и настроить много различных типов функциональности работающей, совместимой VM 5.0; например, вы могли бы получить подробности поведения используемой системы компиляции just-in-time, или получить изображение индикатора хода выполнения службы сборки мусора.

Любое Java-приложение может использовать компоненты платформы, просто получая необходимую ссылку на компонент (используя технику, рассмотренную в данной статье) и затем выполняя соответствующие вызовы методов. В простейшем сценарии клиент компонента может получить информацию о платформе, на которой он работает. Но клиент компонента может также следить за поведением полностью отдельной JVM. Это возможно потому, что MXBean является видом MBean и может удаленно управляться при помощи стандартных JMX-служб, доступных в Java 5.0.


JConsole

Одним из примеров клиента компонента является программа JConsole, поставляемая с Java SE 5.0 SDK. Это - графический интерфейс, подключенный к JVM и используемый для просмотра информации о ней. Закладки в GUI этой программы соответствуют конкретным аспектам JVM; существуют закладки Memory (память), Threads (потоки) и Classes (классы). Программа JConsole предоставляет также суммарную закладку Summary (итоги), отображающую информацию о среде, на которой была запущена VM, и закладку MBeans, при помощи которой пользователь может проконтролировать состояние компонентов MBean платформы более детально.

Запуск JConsole

Вы можете запустить программу JConsole, просто набрав в командной строке команду jconsole (допустим, что каталог bin SDK имеется в вашей переменной path). Введите имя хоста, на котором работает интересующая вас JVM, вместе с номером порта, на котором она ведет прослушивание запросов управления, а также все необходимые параметры аутентификации и нажмите кнопку Connect. Нажатие кнопки Connect со значениями localhost и port 0 по умолчанию приведет к наблюдению за JVM, использовавшейся для запуска самой программы JConsole (поскольку JConsole является Java-процессом). Такой режим известен под названием самоконтроль. На рисунке 2 изображен процесс запуска.

Рисунок 2. Запуск JConsole
JConsole starting up in self-monitoring mode

JConsole в действии

После подключения к JVM, JConsole начинает работу с закладки Summary, как показано на рисунке 3.

Рисунок 3. Закладка Summary программы JConsole
JConsole's Summary tab

С этого момента вы можете выбрать любую другую закладку. Например, закладку Memory, изображенную на рисунке 4, которая показывает историю использования каждого пула памяти в JVM.

Рисунок 4. Закладка Memory программы JConsole
JConsole's Memory tab

Обратите внимание на кнопку Perform GC в правом верхнем углу панели. Это пример одной из многих операций, которые вы можете вызвать на JVM при помощи MBean-компонентов платформы.


Как это работает?

Фундаментом всего выше прочитанного является концепция компонента управления или MBean. Вы можете рассматривать компоненты MBean как программное представление интерфейса управления ресурсом. Говоря проще, вы можете считать их Java-оболочкой вокруг контролируемой сущности. С практической точки зрения MBean-компоненты представляют собой Java-классы, чьи public-методы написаны в соответствии с четко определенным набором правил; эти правила устанавливают полную инкапсуляцию характеристик управляемого приложения или ресурса. В конечном итоге, менеджер ресурса (какой бы он ни был и где бы он ни находился в сети) находит и использует соответствующий MBean-компонент для контроля.

Через свой API MBean-компонент предоставляет информацию о следующем (все изображено на рисунке 5):

  • Текущее состояние ресурса через его свойства
  • Операции с ним, которые могут быть вызваны агентами управления
  • Возможные уведомления о событиях, которые могут быть переданы соответствующим участникам
Рисунок 5. Использование MBean-клиентами свойств, операций и событий
MBean clients make use of properties, operations, and events

Сразу после создания вы должны зарегистрировать MBean-компонент на сервере MBean. Кроме роли реестра MBean-компонентов сервер MBean обеспечивает для систем управления способ поиска и использования зарегистрированных MBean-компонентов. Дополнительная функциональность для управления зарегистрированными MBean-компонентами предоставляется агентскими JMX-службами. В эти службы входят: мониторинг значений атрибутов MBean-компонента и уведомление заинтересованных сторон об их изменениях, периодическое уведомление прослушивателей сообщениями со специфической информацией о MBean-компоненте, поддержка взаимодействий между MBean-компонентами. Агентские JMX-службы сами часто представляют собой MBean-компоненты.

Комбинация MBean-сервера и обязательных агентских JMX-служб называется JMX-агентом (рисунок 6).

Рисунок 6. JMX-агент
The JMX agent

JMX-агент может сделать свои управляемые ресурсы (то есть набор зарегистрированных на данный момент времени MBean-компонентов на MBean-сервере) доступными для других удаленных агентов.

До версии Java 5.0 javax.management API был необязательным расширением платформы Java, которое пользователи могли получить в виде отдельного загружаемого файла и использовать как средство управления и мониторинга ресурсов в Java-коде. Ресурсы в этом контексте могут означать: приложения, J2EE-серверы, выполняющие критические бизнес-приложения, POJO-объекты (plain old Java objects) и даже аппаратные сущности, такие как сетевые устройства, компьютерные приставки, телекоммуникационное оборудование и т.д. Если вы можете сослаться на них из Java-кода, то они потенциально могут стать управляемым ресурсом.

Хотя мы здесь только коснулись темы JMX, мы рассмотрели достаточно для того, чтобы разобраться с MXBean-компонентами. Исчерпывающее обсуждение дизайна JMX и его возможностей выходит за рамки данной статьи. Отличный обзор роли JMX в сетевом управлении приложениями вы можете прочитать в серии статей Синга Ли (Sing Li) по данному предмету (см. раздел Ресурсы).


Что такое MXBean-компоненты и как их можно использовать?

Теперь, когда вы знаете что такое MBean-компоненты, рассмотрим их тезок (почти), определенных в пакете java.lang.management - MXBean-компоненты. Хорошей новостью является то, что MXBean-компоненты не отклоняются от концепций, которые мы привели при обсуждении MBean-компонентов. Большинство типов в этом пакете представляют собой интерфейсы, следующие соглашениям по наименованию, аналогичным применяемым для стандартных MBean-компонентов: имя контролируемого ресурса платформы с суффиксом MXBean. Естественно, для стандартных MBean-компонентов используется суффикс MBean.

В таблице 1 перечислены ресурсы платформы (доступные для контроля через интерфейсы MXBean), предоставленные в пакете java.lang.management:

Таблица 1. Ресурсы платформы, которые могут управляться через MXBean-компоненты
Ресурс платформыСоответствующий MXBean-компонентДоступное количество
КомпиляцияCompilationMXBean0 или 1
Система сборки мусораGarbageCollectorMXBeanПо меньшей мере, 1
ПамятьMemoryMXBeanТочно 1
Менеджеры памятиMemoryManagerMXBeanПо меньшей мере, 1
ПотокиThreadMXBeanТочно 1
Операционная системаOperatingSystemMXBeanТочно 1
Система времени исполненияRuntimeMXBeanТочно 1
Система загрузки классовClassLoadingMXBeanТочно 1
Ресурсы памятиMemoryPoolMXBeanПо меньшей мере, 1

Интерфейс каждого MXBean-компонента, который должен использоваться клиентским приложением, четко определен в спецификации Java 5.0. В настоящее время не существует способа, которым пользователи могли бы настроить интерфейс, для того чтобы он мог предоставлять больше управляемых атрибутов платформы.

Количество возможных экземпляров каждого MXBean-типа, приведенное в третьем столбце таблицы 1, очень зависит от конкретной управляемой системной платформы. Например, спецификация JVM разрешает производителям выбирать любые алгоритмы сбора мусора, и, следовательно, любое количество экземпляров GarbageCollectionMXBean может быть активно одновременно. Сравните их с компонентом OperatingSystemMXBean, который может присутствовать только в одном экземпляре, поскольку управляемая виртуальная машина может, очевидно, работать в конкретный момент времени только на одной операционной системе.

Клиентский код может безопасно полагать, что единичные MXBean-компоненты действительно являются единственными в VM. Когда бы ни была запрошена ссылка на компоненты этого типа, она всегда указывает на один и тот же экземпляр, независимо от того, где и когда был сделан запрос ссылки за время существования VM. Это справедливо даже в тех случаях, когда несколько клиентских приложений выполняет мониторинг конкретной VM.

MBeanServerConnection

Интерфейс javax.management.MBeanServerConnection является супертипом интерфейса javax.management.MBeanServer, который может использоваться для вызова MBean-сервера, если он работает на той же VM, что и клиентское приложение (то есть, если управляющие клиентское приложение и JMX-агент находятся в одной и той же VM). Поскольку между MBeanServerConnection и MBeanServer существуют родственные отношения, клиентские приложения могут использовать вызовы одних и тех же методов для взаимодействия как с удаленным, так и с локальным MBean-сервером.

Что касается клиентского Java-кода, экземпляр MXBean-компонента ведет себя аналогично любому POJO. Информация может быть получена через прямой вызов объекта без участия каких-либо посредников. Естественно, это верно только тогда, когда Java-клиент получил ссылку на локальный компонент (выполняющийся на той же самой VM, что и управляемое приложение) напрямую или запросил прокси-компонент, инкапсулирующий часть удаленной VM. В обоих случаях ссылка берется из единственного для платформы компонента ManagementFactory.

Вы можете также обращаться к компоненту платформы через javax.management.MBeanServerConnection, но в этом случае существует дополнительный уровень косвенности при взаимодействии. Как вы увидите в разделе "Мониторинг удаленной VM через сервер платформы", в этом сценарии клиентские приложения всегда выполняют запросы к MBeanServerConnection для поиска и делают вызовы от их имени в конкретные удаленные компоненты. При этом учитываются приведенные выше рассуждения о JMX, где мы отметили, что для выполнения вызовов к любому удаленному MBean-компоненту удаленные клиентские приложения должны взаимодействовать с MBean-сервером, на котором MBean-компонент зарегистрирован.

MXBean-компоненты - это не JavaBeans

Чтобы не было каких-либо недоразумений, вы должны помнить, что хотя совершенно правильно считать MXBean-компонент видом MBean-компонента, предназначенным для мониторинга и управления JVM, совершенно неверно рассматривать MXBean-компонент как вид JavaBean-компонента. Технология JavaBeans - это компонентная модель для платформы Java, разработанная для предоставления возможности создавать приложения из повторно используемых Java-компонентов при помощи графических средств. Хотя некоторые возможности JavaBeans, например, соглашения о понятном наименовании для упрощения обнаружения свойств программами, присутствуют и в области MBean и MXBean, существует целый мир различий между двумя технологиями. Не путайте их.

Дополнительная информация о MXBean

В начале данной статьи мы говорили о том, что пакет java.lang.management содержит API управления платформой. Мы должны немного уточнить это утверждение, поскольку не все MXBean-компоненты действительно содержатся в этом пакете. Из-за того, что компонент LoggingMXBean настолько внутренне связан с функциональностью ведения журналов платформы Java, был смысл поместить его в пакет java.util.logging. Как видно из его названия, этот тип MXBean-компонента предоставляет интерфейс управления выполнением функций ведения журналов VM. Как указано в справочной документации по этому компоненту, клиентские приложения могут получать имена всех программ ведения журналов (logger), зарегистрированных в платформе, и их взаимоотношения друг с другом. Также есть возможность получить и установить уровень программы ведения журналов с указанием ее имени.

Аналогично компонентам OperatingSystemMXBean и ThreadMXBean (как еще два примера), компонент LoggingMXBean существует в VM в единственном экземпляре. Любые операции чтения и записи его открытых свойств, выполненные любыми средствами, всегда относятся к одному и тому же экземпляру объекта.


Получение MXBean-компонентов

Существует три способа обращения к MXBean-компонентам из клиентского кода: через методы фабрики, через сервер платформы и в качестве прокси-компонента.

Методы фабрики

Простейшим способом извлечения MXBean-компонентов является использование статических методов, предоставленных в классе java.lang.management.ManagementFactory. Однако, полученные таким способом MXBean-компоненты могут быть использованы только для мониторинга вашей локальной VM. Класс ManagementFactory определяет один метод извлечения для каждого типа MXBean-компонента. Некоторые из этих методов возвращают один экземпляр MXBean-компонента, а другие возвращают строго типизированный список (List) экземпляров MXBean-компонентов.

Когда существует только один MXBean-компонент определенного типа, код для извлечения прост. В листинге 1 приведен код для извлечения компонента ThreadMXBean:

Листинг 1. Получение ссылки на единственный для платформы компонент ThreadMXBean
ThreadMXBean threadBean = ManagementFactory.getThreadMXBean();

Для тех типов MXBean-компонентов, для которых может существовать несколько экземпляров, существуют методы фабрики, возвращающие MXBean-компоненты в List, как показано в листинге 2:

Листинг 2. Получение строго типизированного списка всех компонентов MemoryPoolMXBeans, известных платформе
List<MemoryPoolMXBean> memPoolBeans = ManagementFactory.getMemoryPoolMXBeans();
for (MemoryPoolMXBean mpb : memPoolBeans) {
    System.out.println("Memory Pool: " + mpb.getName());
}

Компонент LoggingMXBean является частью пакета java.util.logging, поэтому к нему можно получить доступ при помощи класса LogManager, а не класса ManagementFactory, как показано в листинге 3:

Листинг 3. Получение ссылки на LoggingMXBean из LogManager
LoggingMXBean logBean = LogManager.getLoggingMXBean();

Помните, что эти методы позволяют вам обращаться только к MXBean-компонентам, принадлежащим вашей локальной VM. Если вы хотите расширить область действия вашего клиентского кода на управление удаленными VM, расположенными на той же машине или на другом узле, то должны использовать один из двух следующих методов.

Через сервер платформы

Одним из доступным вам вариантов является структуризация вашего кода для выполнения вызовов через соединение с MBean-сервером на удаленной VM. Для этого вы должны, прежде всего, запустить удаленную VM с основными параметрами командной строки. Они устанавливают порт, через который связанный с ней JMX-агент прослушивает запросы, и уровень необходимой защиты. Например, следующие параметры запускают VM, агент которой прослушивает порт 1234, не используя защищенный режим:

-Dcom.sun.management.jmxremote.port=1234
-Dcom.sun.management.jmxremote.authenticate=false
-Dcom.sun.management.jmxremote.ssl=false

Разрешение защищенного доступа к VM рассматривается в разделе "Безопасность".

При помощи прослушивающего удаленного агента вы могли бы использовать фрагмент кода из листинга 4 для получения ссылки на соответствующее соединение с MBean-сервером:

Листинг 4. Использование JMXConnectorFactory для подключения к MBean-серверу другой VM
try {
    // подключиться к MBeanServer отдельной VM, используя функциональность
    JMXServiceURL address = 
      new JMXServiceURL( "service:jmx:rmi:///jndi/rmi://localhost:1234/jmxrmi");
    JMXConnector connector = JMXConnectorFactory.connect(address);
    MBeanServerConnection mbs = connector.getMBeanServerConnection();
} catch ...

После получения MBeanServerConnection вы можете использовать JMX-методы getAttribute(), setAttribute() и invoke() для работы с MXBean-компонентами. Эта тема рассматривается в разделе "Мониторинг удаленной VM через сервер платформы".

В качестве прокси-компонента

Третий способ доступа к API компонентов управления немного похож на оба уже рассмотренных метода. Как и прежде, вы должны получить MBeanServerConnection к JMX-агенту VM, мониторинг которой хотите осуществлять. Затем, используя статический вспомогательный метод класса ManagementFactory, клиентский код может запросить экземпляр прокси-компонента к именованному MXBean-компоненту, зарегистрированному на MBean-сервере удаленной VM. Пример приведен в листинге 5:

Листинг 5. Ссылка на удаленный MBean-сервер может получить прокси-компоненты к удаленным MXBean-компонентам
try {
    ThreadMXBean threadBean = ManagementFactory.newPlatformMXBeanProxy
        (mbs, ManagementFactory.THREAD_MXBEAN_NAME, ThreadMXBean.class);
} catch ...

Для всех уникальных MXBean-компонентов, за исключением LoggingMXBean, использованные для регистрации на сервере полные строковые имена доступны в полях со спецификаторами public static класса ManagementFactory. То есть, например, строковое представление javax.management.ObjectName для ThreadMXBean хранится в поле THREAD_MXBEAN_NAME. Подобным же образом регистрационное имя для компонента LoggingMXBean хранится в static-поле в классе java.util.logging.LogManager. В листинге 6 приведен запрос на получение экземпляра прокси-объекта для LoggingMXBean:

Листинг 6. Строковое имя компонента LoggingMXBean является константой класса java.util.logging.LogManager
try {
    LoggingMXBean logBean = ManagementFactory.newPlatformMXBeanProxy
        (mbs, LogManager.LOGGING_MXBEAN_NAME, LoggingMXBean.class);
} catch ...

С теми типами MXBean-компонентов, для которых может существовать более одного экземпляра в VM, ситуация становится немного более запутанной. В таких случаях вы сначала должны использовать MBeanServerConnection для получения имен всех зарегистрированных MXBean-компонентов данного типа. Для удобства доменная часть ObjectName каждого MXBean-компонента хранится в полях со спецификаторами public static - ManagementFactory. После извлечения имен каждое из них может быть использовано для создания отдельного экземпляра прокси-компонента. Пример приведен в листинге 7:

Листинг 7. Создание прокси-компонента для каждого MemoryManagerMXBean, принадлежащего удаленной VM
try {
        // Получить имена всех компонентов Memory Manager MXBeans на сервере
        Set srvMemMgrNames = mbs.queryNames(new ObjectName(
            ManagementFactory.MEMORY_MANAGER_MXBEAN_DOMAIN_TYPE + ",*"), null);
        
        // Получить MXBean Proxy для каждого возвращенного имени
        for (Object memMgrName : srvMemMgrNames){
            // Привести тип Object к ObjectName
            ObjectName memMgr = (ObjectName) memMgrName;
            
            // Вызвать newPlatformMXBeanProxy с полным именем объекта
            // для конкретного MXBean-компонента
            MemoryManagerMXBean memMgrBean = 
                ManagementFactory.newPlatformMXBeanProxy(
                    mbs, memMgr.toString(), MemoryManagerMXBean.class);
                    
            // memMgrBean - это прокси-компонент для удаленного MXBean-компонента.
            // Мы можем использовать его  
            // так, как будто он является ссылкой на локальный MXBean-компонент.
            System.out.println("Memory Manager Name = " +
                memMgrBean.getName());
        }
} catch ...

Использование MXBean-компонентов

Определенные каждым MXBean-интерфейсом операции перечислены в документации по java.lang.management. При помощи этих операций пользователь может следить и управлять виртуальной машиной. Например, в MemoryMXBean есть операции, позволяющие разрешить подробный отчет по системе памяти, запросить сборку мусора и извлечь подробную информацию о текущем использовании памяти пулами "кучи" и остальными пулами памяти. Таким образом, если вы интересуетесь количеством используемой Java-приложением памяти или желаете отрегулировать размер "кучи", то можете легко написать управляющее клиентское приложение, используя API java.lang.management для подключения к приложению и для мониторинга использования памяти.

Аналогично, компонент ThreadMXBean предлагает функциональность, которая могла бы быть полезна при зависаниях вашего Java-приложения. Метод findMonitorDeadlockedThreads() возвращает ID всех потоков, которые идентифицирует как зависшие. Вы могли бы затем использовать эти ID для извлечения подробной информации о потоках, включая трассировку их стеков, их состояния, узнать, выполняли ли они собственный код и т.д.

Эта информация о потоке поддерживается в экземпляре класса ThreadInfo, одного из трех классов в пакете управления java.lang, используемого MXBean-компонентами для возврата пользователю мгновенных снимков данных. Остальными двумя классами являются классы MemoryUsage и MemoryNotificationInfo. Каждый из них - это сложный тип данных, содержащий структурированную информацию, которая описывает качества конкретной платформы.

Теперь рассмотрим два примера сценариев, которые демонстрируют, как все вышесказанное перевести в Java-код.


Пример 1: Мониторинг VM через MXBean-компоненты или прокси-компоненты

Как уже упоминалось, методы MXBean-компонентов могут вызываться либо напрямую для локального MXBean-компонента, либо через прокси-компонент. В листинге 8 приведен пример использования операций getter и setter компонента ThreadMXBean. Переменная threadBean в этом примере может быть либо MXBean-компонентом, извлеченным из локальной VM, либо прокси-компонентом для MXBean-компонента на удаленной VM. Она прозрачна для пользователя после получения ссылки.

Листинг 8. Получение и установка значений ThreadMXBean
try {
    // Получить текущий счетчик потоков для JVM
    int threadCount = threadBean.getThreadCount(); 
    System.out.println(" Thread Count = " + threadCount);
       
    // разрешить поток процессорного времени
    threadBean.setThreadCpuTimeEnabled(true);
} catch ...

Метод setThreadCpuTimeEnabled(), используемый в листинге 8, произвольно поддерживается 5.0-совместимыми VM. При использовании этой необязательной функциональности необходима проверка ее существования(листинг 9).

Листинг 9. Проверка поддержки необязательной функциональности перед ее использованием
if (threadBean.isThreadCpuTimeSupported()) {
    threadBean.setThreadCpuTimeEnabled(true);
}

Метод getTotalCompilationTime() компонента CompilationMXBean тоже имеет функциональность, которая не обязательно должна присутствовать в каждой реализации 5.0-совместимой VM. Как и для setThreadCpuTimeEnabled() в листинге 9, есть соответствующий метод, доступный для проверки существования поддержки этой функциональности. Код, который не пользуется этими проверочными методами, должен обрабатывать любые java.lang.UnsupportedOperationExceptions, которые могут быть сгенерированы необязательными методами.

В листинге 10 продемонстрирована реализация доступа к информации обо всех потоках, выполняющихся на VM. Информация о каждом потоке хранится в отдельном объекте ThreadInfo, к которому впоследствии можно обращаться.

Листинг 10. Получение имен всех потоков, выполняющихся в VM
try {
    // Получить идентификаторы всех существующих потоков
    long[] threadIDs = threadBean.getAllThreadIds();
    
    // Получить объект ThreadInfo для каждого threadID
    ThreadInfo[] threadDataset = threadBean.getThreadInfo(threadIDs);
    for (ThreadInfo threadData : threadDataset) {
        if (threadData != null) {
            System.out.println(threadData.getThreadName());
        }
    }
} catch ...

Помните о том, что информация, содержащаяся в таких сложных типах, как ThreadInfo, MemoryUsage и MemoryNotificationInfo, это снимок системы на момент вызова. Эти объекты не обновляются динамически после получения ссылок на них, поэтому, если ваше приложение должно обновлять данные по какому-либо из этих аспектов VM, вы должны выполнить другой вызов для получения обновленных объектов ThreadInfo или MemoryUsage. Объекты MemoryNotificationInfo немного отличаются в этом отношении, поскольку они не извлекаются управляющими приложениями, а помещаются в события уведомления (мы вскоре рассмотрим это более детально).


Пример 2: Мониторинг удаленной VM через сервер платформы

Доступ к ThreadMXBean удаленной JVM с использованием MBeanServerConnection не так прост, как в листинге 1. Во-первых, вам потребуется экземпляр javax.management.ObjectName для ThreadMXBean. Вы можете создать его с тем же строковым именем, что и у прокси-объекта MXBean, как показано в листинге 11:

Листинг 11. Создание ObjectName для ThreadMXBean
try {
    ObjectName srvThrdName = new ObjectName(ManagementFactory.THREAD_MXBEAN_NAME);
    ...
} catch ...

Вы можете использовать экземпляр ObjectName для идентификации конкретного удаленного компонента ThreadMXBean в последующих вызовах методов getAttribute(), setAttribute() и invoke() компонента theMBeanServerConnection, как показано в листинге 12:

Листинг 12. Использование ObjectName в вызовах удаленного MBean-сервера
try {
    // Получить текущее количество потоков для JVM
    int threadCount = 
      ((Integer)mbs.getAttribute( srvThrdName, "ThreadCount")).intValue();
    System.out.println(" Thread Count = " + threadCount);
    
    boolean supported = 
      ((Boolean)mbs.getAttribute(srvThrdName, "ThreadCpuTimeSupported")).booleanValue();
    if (supported) { 
        mbs.setAttribute(srvThrdName, 
          new Attribute("ThreadCpuTimeEnabled", Boolean.TRUE)); 
        ...    
    }
} catch ...

В листинге 13 продемонстрирован доступ к информации обо всех текущих потоках в VM через соединение с MBean-сервером. MXBean-объекты, доступ к которым осуществляется с использованием этого способа, возвращают сложные типы данных путем заключения этого сложного типа в открытый JMX-тип, такой как объект javax.management.openmbean.CompositeData.

Зачем заключать сложные данные в промежуточный тип? Учитывайте то, что MXBean-компоненты потенциально могут управляться удаленными приложениями, написанными не на языке программирования Java, или Java-приложениями, не имеющими доступа ко всем сложным типам, используемым для описания различных характеристик управляемых ресурсов. И хотя можно предположить, что оба конца соединения с JMX-агентом платформы будут понимать такие простые типы как boolean, long и string и будут отображать их в соответствующие типы языка программирования, на котором они реализованы, неправильно предполагать, что сложные типы (например, ThreadInfo или MemoryUsage) будут корректно интерпретироваться всеми возможными управляющими приложениями. Такие открытые типы как CompositeData могут представлять сложные (то есть, не примитивные или структурные) данные в виде фундаментальных типов.

Если вы выполните удаленный вызов к MXBean-компоненту 5.0, требующему передачи экземпляра сложного типа, объект преобразуется в его эквивалент с типом CompositeData. Хотя это и разрешает передачу информации более широкому диапазону клиентских приложений, есть и обратная сторона медали: создание Java-приложений, которые действительно могут разрешить типы ThreadInfo и MemoryUsage, тем не менее, все равно потребует преобразования из открытого типа в сложный тип. Но даже это не является слишком обременительной процедурой, поскольку все поддерживаемые сложные типы данных, определенные в java.lang.management, имеют свои собственные удобные статические методы для такого преобразования.

В листинге 13, атрибут threadDataset содержит массив объектов CompositeData, который прямо отображается в объекты ThreadInfo. Затем, для каждого потока используется static-метод from() объекта ThreadInfo для создания эквивалентного объекта ThreadInfo из CompositeData. Вы можете использовать этот объект для доступа к информации о каждом потоке.

Листинг 13. Тип CompositeData передает сложные структуры данных по сети
try {
    // Получить идентификаторы всех существующих потоков
    long[] threadIDs = (long[])mbs.getAttribute(srvThrdName, "AllThreadIds");
    
    // Получить объект ThreadInfo для каждого threadID. Для этого мы должны 
    // вызвать метод getThreadInfo удаленного компонента потока. 
    // Мы должны передать имя метода для запуска вместе с 
    // аргументом и типом аргумента. Мы знаем, что это довольно криво.
    CompositeData[] threadDataset = 
      (CompositeData[]) (mbs.invoke(srvThrdName, "getThreadInfo",
        new Object[]{threadIDs}, new String[] {"[J"}));
         
    // Восстановить объект ThreadInfo из каждого полученного CompositeData, используя
    // вспомогательный static-метод from(), и затем использовать его для вывода
    // имени потока.     
    for (CompositeData threadCD : threadDataset) {
        ThreadInfo threadData = ThreadInfo.from(threadCD);
        if (threadData != null) {
            System.out.println(threadData.getThreadName());
        }
    }
} catch ...

Поддержка в API

Если вы разрабатываете код управления платформой, проверяющий состояние потоков VM, то можете столкнуться с интересным поведением, которое мы случайно обнаружили при написании этой статьи. Будет ли это в вашем коде? Это зависит от того, насколько интенсивно ваше приложение использует новые механизмы защиты блоков кода от параллельного доступа, представленные в версии 5.0 платформы Java.

В новом пакете java.util.concurrent.locks представлен класс ReentrantLock, который, исходя из его имени, вы можете использовать для создания блокировки с повторными вхождениями для защиты критических секций кода. Это очень напоминает давно известный неявный механизм блокировки, используемый для ключевого слова synchronized, но есть некоторые дополнительные возможности, которые могут быть полезны для более тонкого контроля за явными блокировками. В листинге 14 приведен пример использования этого класса:

Листинг 14. Очень простой пример использования ReentrantLock
private Lock myLock = new ReentrantLock();

...

void myMethod() {
    // Запросить блокировку
    myLock.lock();
    try {
        ... выполнить работу в критической секции ...
    } finally {
        // Освободить блокировку 
        myLock.unlock();
    }// конец блока finally

...

}

Перед входом в критическую секцию вызывается метод lock() объекта ReentrantLock для попытки и получения блокировки. Выполнение будет успешным только в том случае, если другой поток не захватил блокировку, и тогда текущий поток блокируется. До версии 5.0 платформы Java вы могли написать код, аналогичный приведенному в листинге 14, примерно так, как показано в листинге 15. Естественно, вы все еще можете так написать, поскольку слово synchronized не удалено.

Листинг 15. synchronized-метод
synchronized void myMethod() {

... выполнить работу в критической секции ...

}

В этих простых примерах вы не увидите различий в поведении кода. Однако, если вы используете типы ThreadMXBean и ThreadInfo для проверки счетчика блокировок в потоке, который, как вы знаете, будет заблокирован от входа в критическую секцию во время выполнения программы, результаты будут отличаться в зависимости от используемого метода блокировки. Вы можете продемонстрировать это самостоятельно, написав какой-то простой код, имеющий два различных потока, пытающихся вызвать myMethod(), и заставить один из потоков всегда делать это вторым. Этот поток, очевидно, всегда будет блокироваться и должен иметь счетчик блокировок равным единице. Используя ключевое слово synchronized с myMethod(), вы, как и ожидается, увидите в связанном с потоком объекте ThreadInfo, что счетчик блокировок больше нуля. Однако, используя новый подход ReentrantLock, вы увидите, что счетчик блокировок равен нулю. Это связано с тем, что возможности мониторинга VM в ThreadMXBean реализованы в новых параллельных пакетах. Это различие в видимом поведении будет устранено в следующих версиях платформы Java.


Уведомления

Уникальность MemoryMXBean среди MXBean-компонентов заключается в том, что он обладает способностью передавать объектам-клиентам динамические уведомления о событиях, связанных с использованием памяти. Преимущества немедленного взаимодействия такого рода при превышении объема используемой памяти заранее указанного значения очевидны, поскольку это может быть симптомом проблем на прикладном уровне или указанием на то, что требуется дальнейшая настройка виртуальной машины.

Используемая компонентом MemoryMXBean модель уведомлений основана на спецификации JMX MBean, которая, в свою очередь, очень похожа на модель уведомлений, используемую в языке программирования Java. Как передатчик уведомлений MemoryMXBean реализует JMX-интерфейс javax.management.NotificationBroadcaster, относительно маленький интерфейс, разрешающий компоненту регистрировать и снимать регистрацию заинтересованных в уведомлении сторон. В свою очередь, каждая заинтересованная сторона (объект) должна реализовывать интерфейс javax.management.NotificationListener. Этот интерфейс состоит только из одной операции, которая активизируется событием, генерирующим MXBean при возникновении какого-либо события.

Прослушиватели могут быть зарегистрированы (или сняты с регистрации) при помощи компонента MemoryMXBean в любое время на протяжении жизненного цикла VM. Уведомления распространяются только текущему набору зарегистрированных участников.

Когда вызывается метод-обработчик события прослушивателя, он получает экземпляр класса javax.management.Notification. Это общий тип сигнала о событии в модели уведомлений JMX. Он спроектирован таким образом, что может содержать большое количество информации о событии, вызвавшем его создание. Для MemoryMXBean в настоящее время существует два типа уведомлений:

  • Ресурс оперативной памяти (иногда называемый пулом памяти) в VM превышает заранее установленное значение. Этот тип события указывается константой MEMORY_THRESHOLD_EXCEEDED класса MemoryNotificationInfo.
  • Размер ресурса оперативной памяти сразу после сборки мусора превышает заранее установленное значение. Этот тип события указывается константой MEMORY_COLLECTION_THRESHOLD_EXCEEDED класса MemoryNotificationInfo.

При получении объекта Notification в своем методе-обработчике зарегистрированный прослушиватель может определить вид возникшего события, запросив тип объекта Notification и сравнив полученную строку с двумя значениями MemoryNotificationInfo.

Для передачи прослушивателям подробной информации о событии компонент MemoryMXBean устанавливает свойство пользовательских данных каждого сгенерированного объекта (по существу, средство для передатчика для включения любой информации) в конкретный экземпляр javax.management.openmbean.CompositeData, представляющий объект MemoryNotificationInfo. Как мы уже рассматривали в разделе "Мониторинг удаленной VM через сервер платформы", инкапсулирование данных о событии внутри открытого JMX-типа позволяет им быть понятными для широкого набора прослушивателей.


Безопасность

Пока все идет нормально. Теперь пора рассмотреть важнейший вопрос - безопасность. Что, если вы не хотите, чтобы какое-либо приложение могло обратиться и изменить вашу VM? Какие варианты доступны? Существует несколько системных свойств, которые вы можете установить для контроля уровня доступа и способа, каким данные о VM передаются из JMX-агента в управляющее клиентское приложение. Они делятся на две основные категории: аутентификация по паролю и Secure Sockets Layer (SSL).

Использование параметров командной строки

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

-Dcom.sun.management.jmxremote.port=<number>

В нежелательной ситуации, когда вам все равно, кто может обращаться к вашей VM через этот порт, вы можете также отключить аутентификацию по паролю и SSL-шифрование (обе функции включены по умолчанию), добавив два следующих параметра:

-Dcom.sun.management.jmxremote.authenticate=false
-Dcom.sun.management.jmxremote.ssl=false

Совместное использование трех этих параметров удобно, когда вы разрабатываете ваш клиентский код java.lang.management и хотите иметь возможность легко следить за другой VM. В рабочей среде вы должны установить либо проверку пароля, либо SSL (либо, возможно, и то, и другое).

Аутентификация по паролю

В каталоге jre/lib/management вашего комплекта 5.0 SDK имеется файл под названием jmxremote.password.template. Он определяет имена пользователей и пароли для двух ролей. Первая - это роль мониторинга, разрешающая доступ только к читающим информацию функциям управления; вторая - роль управления для записывающих функций. Клиенты могут выполнять аутентификацию с именами пользователя либо monitorRole, либо controlRole в зависимости от требуемого уровня доступа. Для гарантирования того, что только эти аутентифицированные пользователи могут иметь доступ, вы должны выполнить следующие действия:

  1. Скопировать содержимое jmxremote.password.template в файл под названием jmxremote.password и снять комментарии с последних строк файла, определяющих имена пользователей и пароли, и при желании изменить пароли.
  2. Изменить права доступа к файлу jmxremote.password таким образом, чтобы он мог читаться и меняться только его владельцем. В UNIX и UNIX-подобных операционных системах установите права доступа в 600. В Microsoft Windows следуйте инструкциям, приведенным в статье "Как защитить файл password в операционных системах Microsoft Windows", ссылка на которую приведена в разделе Ресурсы.)
  3. При запуске VM укажите месторасположение используемого password-файла в следующем параметре командной строки:

    -Dcom.sun.management.jmxremote.password.file=<file-path>

С точки зрения управляющего клиентского приложения вы должны предоставить корректную комбинацию username/password для доступа к VM, для которой включена аутентификация по паролю. Если клиентом является программа JConsole, сделать это просто: на первой закладке Connection предоставлены поля username и password. Для написания кода, предоставляющего информацию для аутентификации на удаленной VM, вы должны добавить изменения, приведенные в листинге 16, к первоначальному коду подключения из листинга 4:

Листинг 16. Подключение к удаленной VM, требующей аутентификации пользователя
try {
    // предоставить корректные имя пользователя и пароль
    // (например, через аргументы программы)
    String user = "monitorRole";
    String pw = "password";
    
    // поместить имя пользователя и пароль в строковый массив credentials, который 
    // может быть использован при подключении к удаленному JMX-агенту
    String[] credentials = new String[] { user, pw };

    // строковый массив credentials поместить в map со значением ключа, равным  
    // строго заданной строке идентификатора полномочий 
    Map<String, String[]> props = new HashMap<String, String[]>();
    props.put("jmx.remote.credentials", credentials);

    // предоставить карту полномочий методу connect 
    JMXServiceURL address = 
      new JMXServiceURL("service:jmx:rmi:///jndi/rmi://localhost:1234/jmxrmi");
    JMXConnector connector = JMXConnectorFactory.connect(address, props);
    
    // тривиальная задача получения ссылки на соединение MBean-сервера  
    // с удаленным агентом 
    MBeanServerConnection mbs = connector.getMBeanServerConnection();
} catch ...

Указание неправильного имени пользователя или пароля виртуальной машине, требующей аутентификации, приводит к генерированию исключительной ситуации java.lang.SecurityException. Аналогично, аутентификация с ролью monitorRole и последующий вызов одной из операций чтение/запись (например, запрос на сборку мусора) тоже приведет к генерированию SecurityException.

Использование SSL

Вы можете использовать SSL для шифрования информации, передаваемой из JMX-агента платформы управляющему клиентскому приложению, осуществляющему ее мониторинг. Передаваемые данные шифруются при помощи криптографии с открытым ключом (ассиметричным), поэтому только владелец соответствующего личного ключа может расшифровать данные. Это запрещает перехватывающим пакеты приложениям просмотр содержимого пакетов во время передачи информации. Использование этой возможности требует настройки SSL на обеих взаимодействующих сторонах, что включает в себя генерирование пары ключей и цифрового сертификата. Подробная информация по этой теме выходит за рамки данной статьи; прочтите, пожалуйста, отличное руководство Грега Трависа (Greg Travis) "Использование JSSE для защиты коммуникации между сокетами" (см. раздел Ресурсы).

Хорошей новостью является то, что после создания пары ключей и сертификата вам не нужно менять ваш управляющий код для использования SSL. Вы можете разрешить шифрование простым указанием определенных параметров командной строки. Во-первых, Java-приложение, которым вы хотите управлять или выполнять мониторинг, должно быть запущено со следующими параметрами:

-Dcom.sun.management.jmxremote.ssl.need.client.auth=true
-Djavax.net.ssl.keyStore=<keystore-location>
-Djavax.net.ssl.trustStore=<truststore-location>
-Djavax.net.ssl.keyStoreType=<keystore-type>
-Djavax.net.ssl.keyStorePassword=<keystore-password>
-Djavax.net.ssl.trustStoreType=<truststore-type>
-Djavax.net.ssl.trustStorePassword=<truststore-password>

Управляющие клиентские приложения, желающие взаимодействовать с платформой, должны быть запущены с подмножеством указанных выше параметров: вы можете опустить первую и две последние строчки. Если клиентским приложением является JConsole, вы можете передать эти параметры при запуске GUI, используя синтаксис командной строки -J, через который передаются Java-параметры в JVM.

Опять же, руководство "Использование JSSE для защиты коммуникации между сокетами" предоставляет более подробную информацию по этим параметрам.


Заключение

Мы надеемся, что побудили вас к поиску дополнительной информации по API управления платформой Java 5.0. Так же как появление Java Management Extensions обеспечило разработчиков и администраторов корпоративных Java-приложений стандартным средством мониторинга и управления их развертыванием, введение java.lang.management API в Java 5.0 предоставило средства, дающие возможность заглянуть во внутренности той же платформы, на которой выполняются ваши приложения.

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

Поскольку "диагностируемость, мониторинг и управление" являются одними из ключевых принципов ожидаемой версии 6.0 платформы Java, этот API начинает играть все более важную роль в будущем Java-технологии.

Ресурсы

Научиться

Получить продукты и технологии

  • MBeanInspector: Пользователи WebSphere 5 могут загрузить этот файл для исследования MBean-компонентов, зарегистрированных на сервере приложений.

Обсудить

Комментарии

developerWorks: Войти

Обязательные поля отмечены звездочкой (*).


Нужен IBM ID?
Забыли Ваш IBM ID?


Забыли Ваш пароль?
Изменить пароль

Нажимая Отправить, Вы принимаете Условия использования developerWorks.

 


Профиль создается, когда вы первый раз заходите в developerWorks. Информация в вашем профиле (имя, страна / регион, название компании) отображается для всех пользователей и будет сопровождать любой опубликованный вами контент пока вы специально не укажите скрыть название вашей компании. Вы можете обновить ваш IBM аккаунт в любое время.

Вся введенная информация защищена.

Выберите имя, которое будет отображаться на экране



При первом входе в developerWorks для Вас будет создан профиль и Вам нужно будет выбрать Отображаемое имя. Оно будет выводиться рядом с контентом, опубликованным Вами в developerWorks.

Отображаемое имя должно иметь длину от 3 символов до 31 символа. Ваше Имя в системе должно быть уникальным. В качестве имени по соображениям приватности нельзя использовать контактный e-mail.

Обязательные поля отмечены звездочкой (*).

(Отображаемое имя должно иметь длину от 3 символов до 31 символа.)

Нажимая Отправить, Вы принимаете Условия использования developerWorks.

 


Вся введенная информация защищена.


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=40
Zone=Технология Java
ArticleID=146011
ArticleTitle=Использование компонентов управления платформой Java
publish-date=04112006