Теория и практика Java: оснащение приложений средствами JMX

Все сразу станет видно -- просто добавьте bean-компоненты

На поведение приложения могут пролить свет отладчики и профайлеры, но использование этих инструментов часто оставляют до возникновения серьезной проблемы. Понять, что же делают программы без применения отладчика, помогут встраиваемые в приложение контрольные ловушки. Теперь, когда в платформу Java™ SE встраиваются расширения для управления Java (JMX -- Java Management Extensions), а средство просмотра jconsole предоставляет универсальный графический интерфейс пользователя (GUI -- Graphic User Interface) для мониторинга, использование JMX для контроля за приложением стало легче и эффективнее.

Брайан Гоетц, старший штатный инженер, Sun Microsystems Inc.

Брайан Гоетц (Brian Goetz) уже 20 лет профессионально работает разработчиком программного обеспечения. В компании Sun Microsystems он занимает должность старшего штатного инженера и входит в состав нескольких экспертных групп JCP. В мае 2006 года издательство Addison-Wesley опубликовало книгу Брайана под названием Java Concurrency In Practice (Параллелизм Java на практике). См. также опубликованные и готовящиеся статьи Брайана в популярных отраслевых изданиях



19.09.2006

Сколько раз, глядя на работающее приложение, вы гадали: "Какого черта оно там делает, и почему это длится так долго?" В такие моменты, возможно, хочется иметь побольше встроенных в приложение возможностей для мониторинга. Например, для серверного приложения хорошо было бы просматривать количество и типы задач, стоящих в очереди на обработку, задач в процессе обработки, статистику производительности за последнюю минуту или час, среднее время обработки задачи, и т.д. Такую статистику легко собрать, но без неагрессивных средств извлечения нужных данных она не очень полезна.

Экспортировать рабочие данные можно многими способами. Можно периодически записывать снимки статистики в журнальный файл, создать Swing GUI, использовать встроенный HTTP-сервер для отображения статистики на Web-странице, или опубликовать Web-службу, которую можно использовать для запроса статуса приложения. Но в отсутствие инфраструктуры мониторинга и публикации данных большинство разработчиков приложений не используют столь затратные процедуры. В результате не удается достигнуть той степени контроля за работой приложения, которую хотелось бы иметь.

JMX

Что же касается Java 5.0, то библиотека классов и JVM предоставляют всеобъемлющую инфраструктуру управления и мониторинга -- JMX. JMX -- это стандартизованные средства предоставления интерфейса управления с возможностью удаленного доступа. Это удобный способ добавить в приложение гибкий и мощный интерфейс управления. JMX-компоненты, называемые управляемыми компонентами (MBeans -- managed beans), представляют собой JavaBean-компоненты, которые предоставляют средства доступа и бизнес-методы, относящиеся к управлению объектом. Каждый управляемый объект (это может быть целое приложение или служба приложения) создает MBean-компонент и регистрирует его с использованием удобочитаемого для человека названия. Работа приложения с поддержкой JMX основывается на сервере MBeanServer, который действует как контейнер для MBean-компонентов и предоставляет службы для удаленного доступа, управления пространством имен и обеспечения безопасности. На стороне клиента средство jconsole может работать в качестве универсального JMX-клиента. Вся эта платформа поддержки JMX, вместе взятая, значительно сокращает усилия, необходимые для поддержки приложением внешнего интерфейса управления.

Кроме предоставления реализации MBeanServer, Java SE 5.0 также обеспечивает предоставление виртуальной машиной Java (JVM) средств просмотра состояния системы управления памятью, загрузки классов, активных потоков, журналирования и конфигурации платформы. Мониторинг и управление для большинства служб платформы по умолчанию включены (на производительность это влияет минимально), так что лишь остается подключиться к приложению с помощью JMX-клиента. На рисунке 1 показана консоль jconsole JMX-клиента (входит в состав JDK), отображающая одно из представлений системы управления памятью -- использование стека на протяжении определенного времени. Наличие кнопки Perform GC демонстрирует возможность в дополнение к просмотру оперативной статистики инициировать с помощью JMX различные операции.

Рисунок 1. Применение jconsole для просмотра использования стека
Применение jconsole для просмотра использования стека

Транспорты и безопасность

JMX определяет протокол, который используется для обмена данными между MBeanServer JMX-клиентом и может работать поверх различных транспортов. Можно использовать встроенный транспорт для локальных соединений, либо удаленные транспорты с использованием RMI, сокетов или SSL. Также возможно создавать новые транспорты с помощью JMX Connector API. Аутентификация обеспечивается транспортом в обязательном порядке. Локальный транспорт позволяет подключаться к JVM, работающим на локальной системе под тем же самым идентификатором пользователя, а удаленные транспорты могут требовать аутентификации с использованием паролей или сертификатов. В Java 6 локальный транспорт включен по умолчанию. Чтобы включить его в Java 5.0, необходимо определить системное свойство com.sun.management.jmxremote при запуске JVM. Документ "Мониторинг и управление с помощью JMX" (см. "Ресурсы") описывает этапы конфигурации для включения и настройки транспортов.


Оснащение Web-сервера

Оснащение приложения средствами для использования JMX осуществляется просто. Подобно многим другим средам удаленного выполнения (RMI, EJB и JAX-RPC), средства JMX основаны на технологии интерфейсов. Для создания управляемой службы необходимо создать интерфейс MBean, определяющий методы управления. После этого можно создать MBean-компонент, который реализует этот интерфейс, инициализирует его и регистрирует с помощью MBeanServer.

Листинг 1 демонстрирует интерфейс MBean для такой сетевой службы, как Web-сервер. В листинге организуются методы типа getter для извлечения конфигурационной (например, номер порта) и рабочей информации (например, запущена ли служба). Также листинг содержит методы getter и setter для просмотра и изменения настраиваемых параметров (например, текущего уровня журналирования), и такие методы для выполнения управляющих операций, как start() и stop().

Листинг 1. Интерфейс MBean для Web-сервера
public interface WebServerMBean {
    public int getPort();

    public String getLogLevel();
    public void setLogLevel(String level);

    public boolean isStarted();
    public void stop();
    public void start();
}

Реализация класса MBean обычно довольно прямолинейна, поскольку предполагается, что интерфейс MBean отражает свойства и операции по управлению существующего объекта или службы. Например, методы getLogLevel() и setLogLevel() в MBean просто осуществляют перенаправление на методы getLevel() и setLevel() объекта Logger, который используется Web-сервером. JMX налагает некоторые ограничения на имена. Например, имена интерфейса MBean должны заканчиваться на MBean, а класс MBean для интерфейса FooMBean должен называться Foo. (Это ограничение можно обойти путем использования расширенных возможностей JMX, динамических MBean-компонентов.) Регистрация MBean-компонента с MBeanServer по умолчанию также проста и показана в листинге 2:

Листинг 2. Регистрация MBean-компонента с помощью встроенной реализации JMX
  public class WebServer implements WebServerMBean { ... }

  ...

  WebServer ws = new WebServer(...);
  MBeanServer server = ManagementFactory.getPlatformMBeanServer();
  server.registerMBean(ws, new ObjectName("myapp:type=webserver,name=Port 8080"));

Параметр ObjectName, передаваемый в registerMBean(), идентифицирует управляемый объект. Поскольку ожидается, что данное приложение может содержать много управляемых объектов, то имя содержит домен ("myapp" в листинге 2), а также некоторое количество пар "ключ-значение", определяющих управляемый ресурс внутри домена. Ключи "name" ("имя") и "type" ("тип") используются часто. Когда они присутствуют, имя должно уникально определять управляемый объект среди всех MBean-компонентов этого же типа в пределах данного домена. Можно также указывать и другие пары "ключ-значение", а в JMX API предусмотрены средства для сопоставления имен объектов заданным шаблонам.

После создания и регистрации MBean-компонента можно сразу запускать jconsole для контроля приложения (наберите jconsole в командной строке) и просмотра его атрибутов и операций управления в представлении "MBeans". На рисунке 2 показана закладка Attributes ("Атрибуты") в jconsole для нового MBean-компонента, а на рисунке 3 показана закладка Operations ("Операции"). С помощью отражения JMX проясняет, какие свойства доступны только для чтения (Started, Port), и какие -- для чтения и записи (LogLevel), а jconsole позволяет изменять доступные для записи свойства. Если метод setter для свойства, доступного для чтения и записи, обрабатывает исключительную ситуацию (например, IllegalArgumentException), JMX предоставляет об этом отчет клиенту.

Рисунок 2. Закладка Attributes в jconsole MBean-компонента
Закладка Attributes в jconsole MBean-компонента
Рисунок 3. Закладка Operations в jconsole MBean-компонента
Закладка Operations в jconsole MBean-компонента

Типы данных

Средства доступа и операции в MBean-компонентах могут использовать в своих сигнатурах любой примитивный тип, а также String, Date и любые стандартные классы библиотек. Также можно использовать массивы и коллекции этих разрешенных типов. Методы MBean могут также использовать другие упорядочиваемые типы данных, но это может создать проблемы функциональной совместимости, поскольку файлы классов необходимо сделать доступными также и JMX-клиенту. (При использовании транспорта RMI для этой задачи можно использовать встроенные в RMI возможности автоматической загрузки классов.) Если в интерфейсе управления используются структурированные типы данных, то во избежание проблем функциональной совместимости, связанных с доступностью классов, можно использовать функцию JMX под названием Open MBeans, которая служит для представления составных или табличных данных.


Оснащение серверного приложения

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

Если JMX используется для получения информации о том, что делает серверное приложение, необходимы средства для идентификации и отслеживания элементов работы. Если для описания задач используются стандартные интерфейсы Runnable и Callable, то самоописательные классы задач (например, реализация метода с осмысленным названием toString()), помогут отслеживать прохождение задач в жизненном цикле и предоставят методы MBean для возврата списков как законченных задач, так и находящихся в состоянии ожидания либо обработки.

TrackingThreadPool в листинге 3 иллюстрирует подкласс для ThreadPoolExecutor, который отслеживает задачи в процессе исполнения, а также осуществляет хронометраж для завершенных задач. Он выполняет эти задачи путем замещения ловушек beforeExecute() и afterExecute() и предоставления методов типа getter для получения собранных данных.

Листинг 3. Класс пула потоков, который собирает статистику о задачах в процессе исполнения и среднем времени, затраченном на задачу
public class TrackingThreadPool extends ThreadPoolExecutor {
    private final Map<Runnable, Boolean> inProgress 
        = new ConcurrentHashMap<Runnable,Boolean>();
    private final ThreadLocal<Long> startTime = new ThreadLocal<Long>();
    private long totalTime;
    private int totalTasks;

    public TrackingThreadPool(int corePoolSize, int maximumPoolSize, long keepAliveTime,
       TimeUnit unit, BlockingQueue<Runnable> workQueue) {
        super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue);
    }

    protected void beforeExecute(Thread t, Runnable r) {
        super.beforeExecute(t, r);
        inProgress.put(r, Boolean.TRUE);
        startTime.set(new Long(System.currentTimeMillis()));
    }

    protected void afterExecute(Runnable r, Throwable t) {
        long time = System.currentTimeMillis() - startTime.get().longValue();
        synchronized (this) {
            totalTime += time;
            ++totalTasks;
        }
        inProgress.remove(r);
        super.afterExecute(r, t);
    }

    public Set<Runnable> getInProgressTasks() {
        return Collections.unmodifiableSet(inProgress.keySet());
    }

    public synchronized int getTotalTasks() {
        return totalTasks;
    }

    public synchronized double getAverageTaskTime() {
        return (totalTasks == 0) ? 0 : totalTime / totalTasks;
    }
}

ThreadPoolStatusMBean в листинге 4 демонстрирует интерфейс MBean для TrackingThreadPool, предоставляющий количество активных задач, активных потоков, завершенных задач, задач в состоянии ожидания, и список задач, в настоящее время ожидающих выполнения или находящихся в процессе выполнения. Включение в интерфейс управления списка задач в состоянии ожидания и выполнения позволяет видеть не только степень загруженности приложения, но и чем оно в данный момент занято. Эта возможность позволяет получить представление не только о поведении приложения, но также и о природе набора данных, который в данный момент обрабатывается.

Листинг 4. Интерфейс MBean для TrackingThreadPool
public interface ThreadPoolStatusMBean {
    public int getActiveThreads();
    public int getActiveTasks();
    public int getTotalTasks();
    public int getQueuedTasks();
    public double getAverageTaskTime();
    public String[] getActiveTaskNames();
    public String[] getQueuedTaskNames();
}

Если задачи достаточно тяжеловесные, то, возможно, имеет смысл пойти дальше и зарегистрировать MBean-компонент для каждой задачи при ее передаче на исполнение (и отменять регистрацию после ее завершения). После этого можно использовать интерфейс управления для запроса статуса задачи, оценки времени ее выполнения или запроса на отмену задачи.

ThreadPoolStatus в листинге 5 реализует интерфейс ThreadPoolStatusMBean, предоставляющий очевидные реализации для каждого средства доступа. Как обычно для реализации классов MBean, каждая операция тривиальна для исполнения и делегирует полномочия управляемому объекту нижнего уровня. В данном примере код JMX полностью отделен от кода управляемого объекта. TrackingThreadPool ничего не знает о JMX. Он предоставляет свой собственный интерфейс управления посредством методов управления и средств доступа для соответствующих атрибутов. Можно выбирать между исполнением функций управления прямо в классе реализации (т.е. заставить TrackingThreadPool реализовать интерфейс TrackingThreadPoolMBean) и отдельным их исполнением (как в листингах 4 и 5).

Листинг 5. Реализация MBean для TrackingThreadpool
public class ThreadPoolStatus implements ThreadPoolStatusMBean {
    private final TrackingThreadPool pool;

    public ThreadPoolStatus(TrackingThreadPool pool) {
        this.pool = pool;
    }

    public int getActiveThreads() {
        return pool.getPoolSize();
    }

    public int getActiveTasks() {
        return pool.getActiveCount();
    }

    public int getTotalTasks() {
        return pool.getTotalTasks();
    }

    public int getQueuedTasks() {
        return pool.getQueue().size();
    }

    public double getAverageTaskTime() {
        return pool.getAverageTaskTime();
    }

    public String[] getActiveTaskNames() {
        return toStringArray(pool.getInProgressTasks());
    }

    public String[] getQueuedTaskNames() {
        return toStringArray(pool.getQueue());
    }

    private String[] toStringArray(Collection<Runnable> collection) {
        ArrayList<String> list = new ArrayList<String>();
        for (Runnable r : collection)
            list.add(r.toString());
        return list.toArray(new String[0]);
    }
}

Для иллюстрации того, как эти классы могут обеспечивать представление выполняемых приложением задач, рассмотрим приложение для поиска в Web, работа которого делится на два типа задач: извлечение удаленных страниц и их индексация. Каждая задача описывается с помощью FetchTask либо IndexTask, как показано в листинге 6. Можно создать MBean-компонент ThreadPoolStatus, который будет обеспечивать интерфейс управления для пула потоков, используемого для обработки этих задач и регистрации их с помощью JMX.

Листинг 6. Класс FetchTask, используемый в приложении для поиска в Web
public class FetchTask implements Runnable {
    private final String name;

    public FetchTask(String name) {
        this.name = name;
    }

    public String toString() {
        return "FetchTask: " + name;
    }

    public void run() {  /* Fetch remote resource */  }
}

По мере обработки приложением каждой страницы в очередь могут помещаться новые задачи для извлечения страниц, на которые имеются ссылки с текущей страницы. Так что в любой заданный момент времени в очереди присутствует смесь задач на извлечение и индексацию, ожидающих своего выполнения. Возможность точного определения, какие страницы находятся в процессе обработки или ожидания позволяет узнать не только характеристики производительности приложения, но также и характеристики обрабатываемых данных.

На рисунке 4 показан снимок приложения для поиска в Web, которое обрабатывает сайт whitehouse.gov. Можно увидеть, что домашняя страница уже извлечена и проиндексирована, и приложение работает над извлечением и индексацией страниц, на которые были прямые ссылки с домашней страницы. Нажав кнопку Refresh ("Обновить"), можно оценить прохождение задач в приложении. Это даст много информации о том, как приложение работает, без ведения интенсивного журналирования или запуска программы в отладчике.

Рисунок 4. Активные и поставленные в очередь задачи в приложении для поиска в Web
Активные и поставленные в очередь задачи в приложении для поиска в Web

Заключение

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

Ресурсы

Научиться

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

Обсудить

Комментарии

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=174480
ArticleTitle=Теория и практика Java: оснащение приложений средствами JMX
publish-date=09192006