JMX Hello World

Рубрика: Java | 8 November 2007, 15:41 | juriy

Давно хотел набросать пару строк про JMX, но никак не доходили руки. Теперь, как говорится, жизнь заставила.

В первый раз я работал с этой технологией в проекте, где обязательным требованием было использование Java 1.4. А в этой версии, как известно, встроенной поддержки JMX нету. Благодаря этому факту, и еще нескольким нюансам у меня сложилось впечатление, что JMX это до ужаса сложная штуковина и работать с ней нормальный человек все равно не сможет.

На самом деле в JMX нету абсолютно ничего страшного. На то чтобы начать использовать эту технологию в своем проекте потребуется не больше трех часов. А с моей заметкой – максимум 40 минут ;-).

Итак, начнем сначала. JMX – Java Management Extensions – API при помощи которого можно контролировать работу приложений и управлять различными параметрами удаленно в реальном времени. JMX вошла в поставку Java начиная с версии 5.

Начнем с простой демонстрации. Выполняем следующие действия:

>cd %JAVA_HOME%/demo/jfc/Notepad

>java -Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.port=8085 -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.ssl=false -jar Notepad.jar

Не пугайтесь изобилия параметров, они не страшные, если вчитаться в имена. Мы указываем виртуальной машине, что необходимо “подключить” поддержку JMX, использовать для общения порт 8085, отключить аутентификацию и ssl.

Должен запуститься пример из поставки Java – блокнот, написанный с использованием SWING. Заметим, что в самом приложении Notepad нет ни строки кода, который работает с JMX.

Во втором терминале, или вообще на другом компьютере, запускаем jconsole. Эта утилита тоже входит в стандартную поставку и служит для работы с JMX серверами (а запущенный блокнот как раз таковым и является, благодаря установленным параметрам). В Java 6 jconsole стала намного удобнее и приятнее на вид, так что, если есть возможность, я бы посоветовал использовать именно ее, а не jconsole из версии Java 5.

В открывшемся окне выбираем Remote Process и вводим localhost:8085. Для тех, кто использует Java 5: заходим на вкладку Remote, указываем host: localhost, port: 8085.

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

Зайдите на вкладку MBeans и поиграйтесь с параметрами виртуальной машины. Найдите, к примеру, ветку java.lang->ClassLoading->Attributes и посмотрите, сколько классов загрузила виртуальная машина. Во вкладке java.lang->Memory->Operations можно выполнить операцию gc – вызвать на удаленной виртуальной машине garbage collector.

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

Начнем с простого Java приложения и посмотрим, что может дать использование JMX.

Приложение, которое я использовал для теста – сервер, который запускает пул потоков, которые поочередно обрабатывают простые задачи. Иногда задач может быть больше чем свободных потоков, в таком случае некоторые задачи не будут выполняться вовсе. Иногда потоков может оказаться больше чем нужно для выполнения задачи – тогда “лишние” потоки будут остановлены.

Модель приложения похожа на обычный HTTP сервер или сервлет контейнер вроде Tomcat. В Tomcat потоки обрабатывают запросы пользователей, а мой сервер будет обрабатывать работы-болванки.

Вот исходник:
[java]

package com.juriy.server;
import com.juriy.mbeans.*;
import javax.management.*;
import java.util.concurrent.*;
import java.util.*;
import java.lang.management.*;
/**
 * @author Yuriy Bura
 * @version $Id: $
 */
public class TestServer {
  private ThreadPoolExecutor executor;
  private int rejected = 0;

  public TestServer(ThreadPoolExecutor executor) {
    this.executor = executor;
  }

  private static class Task implements Runnable {
    public void run() {
      Random random = new Random();
      int numLoops = Math.abs(random.nextInt()%5);
      for (int i = 0; i < numLoops; i++) {
        try {
          System.out.println("Running in [" + Thread.currentThread().getName() + "]");
          Thread.sleep(Math.abs(random.nextInt()%5000));
        } catch(Exception e) {
          System.out.println("Interrupted");
        }
    }
  }
}

public void runServer()	{
  executor.prestartAllCoreThreads();
  while (true) {
    try {
      executor.execute(new Task());
      Thread.sleep(1000);
    } catch (InterruptedException e) {}
    } catch(RejectedExecutionException e) {
      rejected++;
    }
  }
}

public ThreadPoolExecutor getExecutor()	{
  return executor;
}

public int getRejected() {
 return rejected;
}

public void flushRejected() {
  rejected = 0;
}

public static void main(String[] args) throws Exception {
  ThreadPoolExecutor executor = new ThreadPoolExecutor(
    3, 5, 10L,
    TimeUnit.SECONDS,
    new SynchronousQueue < Runnable > ()
  );
  executor.prestartAllCoreThreads();
  TestServer server = new TestServer(executor);
  server.runServer();
 }
}

[/java]
Обратите внимание: эта программа ничего не делает для работы с JMX.

Основа JMX – это Managed Beans или просто MBeans. Чтобы “опубликовать” методы через JMX необходимо сделать 3 шага:

Шаг 1. Описать интерфейс MBean’а в интерфейсе. По принятым соглашениям интерфейс должен называться InterfaceNameMBean. В нашем примере, стоит задача управления сервером, поэтому интерфейс будет называться ServerControllerMBean.

Вот так будет выглядеть интерфейс:

[java]

package com.juriy.mbeans;
/**
* @author Yuriy Bura
* @version $Id: $
*/
public interface ServerControllerMBean{
  public int getCorePoolSize();
  public void setCorePoolSize(int corePoolSize);
  public int getMaxPoolSize();
  public void setMaxPoolSize(int maxPoolSize);
  public int getRejectedCount();
  public int getActiveThreads();
  public int getPassiveThreads();
  public int getTotalThreads();
  public void flushRejected();
}

[/java]

Как видно, MBean предоставляет полный доступ к двум переменным сorePoolSize и maxPoolSize. Кроме того, есть 4 переменных, которые можно считывать: rejectedCount, activeThreads, passiveThreads, totalThreads и метод flushRejected, который, естественно, можно вызывать.

Второй шаг – написать реализацию интерфейса. Тут тоже нет ничего сверхъестественного:

[java]

package com.juriy.mbeans;
import com.juriy.server.*;
/**
* @author Yuriy Bura
* @version $Id: $
*/
public class ServerController implements ServerControllerMBean {
  private TestServer server;
  public ServerController(TestServer server) {
    this.server = server;
  }

  public int getCorePoolSize() {
    return server.getExecutor().getCorePoolSize();
  }

  public void setCorePoolSize(int corePoolSize) {
    server.getExecutor().setCorePoolSize(corePoolSize);
  }

  public int getMaxPoolSize()	{
    return server.getExecutor().getMaximumPoolSize();
  }

  public void setMaxPoolSize(int maxPoolSize)	{
    server.getExecutor().setMaximumPoolSize(maxPoolSize);
  }

  public int getRejectedCount() {
    return server.getRejected();
  }

  public int getActiveThreads() {
    return server.getExecutor().getActiveCount();
  }

  public int getPassiveThreads() {
    return server.getExecutor().getPoolSize() - server.getExecutor().getActiveCount();
  }

  public int getTotalThreads() {
    return server.getExecutor().getPoolSize();
  }

  public void flushRejected() {
    server.flushRejected();
  }
}

[/java]
Как видно, ServerController это просто обертка вокруг класса TestServer, единственное назначение которой – обеспечить публикацию методов и переменных через JMX.

Третий шаг – зарегистрировать только что созданный MBean. Для этого в метод main добавим такие строки:

[java]

ServerController serverController = new ServerController(server);
MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
ObjectName name = new ObjectName("com.juriy.mbeans:type=ServerController");
mbs.registerMBean(serverController, name);

[/java]

Вот, собственно, и все. Запускаем программку с теми-же параметрами что и блокнот:

>java -Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.port=8085 -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.ssl=false com.juriy.server.TestServer

Подключаемся к localhost:8085 при помощи jconsole. Во вкладке MBeans выбираем com.juriy.mbeans->ServerController->Attributes. Смотрим на значение rejectedCount и ужасаемся, увеличиваем размер пула. Устанавливаем maxPoolSize = 10 и выполняем flushRejected (com.juriy.mbeans->ServerController->Operations). Убеждаемся, что сервер больше не “отбрасывает” задачи.

Вот и все, задача выполнена минимальными усилиями. Управлять таким сервером можно как с локальной так и с удаленной машины. Обратите внимание: класс сервера так и не узнал о том, что кто-то будет им управлять. Никаких дополнительных GUI утилит для управления сервером писать не пришлось – хватило стандартной jconsole.

В JMX есть еще масса полезных возможносей. То что я показал – только вершина айсберга. Но это уже совсем другая история.

Пример из статьи:
JMX demo application

Чтобы запустить пример, разархивируйте jmxtest.rar и выполните ant run.

Комментариев: 9

9 Responses to “JMX Hello World”

Комментарии:

  1. Andrew Perepelytsya

    Классика – JMS is not JMX, проверь текст статьи.

    Второе – упорное желание авторов пост-сов пространства (не нашел лучшего слова) навязать rar всему миру. Дело не в том, кто лучше, а в том, кто был первым и какие инструменты доступны. Многие просто не станут скачивать rar, потому что нужет “еще один” распаковщик. Не очень критично в этой статье, но в целом это нужно учитывать.

    Да, и добро пожаловать в JMX world :)

  2. Vadim Voituk

    Андрей, судя по всему уже поправил опечатки :)

  3. garnash

    > Обратите внимание: эта программа ничего не делает для работы с JMX.

    Первый листинг уже сожержит четвертый листинг.

  4. juriy

    Спасибо всем за комменты:

    > JMS is not JMX
    Согласен на 100% – вот так бывает, когда пишешь об одном, а думаешь о другом.

    > Первый листинг уже сожержит четвертый листинг.
    Поправили, ошибочка вышла, спасибо.

  5. Denis

    Спасибо. Супер.

  6. Alex

    Спасибо, очень помогло.

  7. avc

    Во вкладке MBeans выбираем com.juriy.mbeans->ServerController->Attributes.
    после запуска приложения и подключения к серверу в jconsole во вкладке Mbeans я так и не обнаружил классов com.juriy.mbeans – что я сделал не так ? )

  8. Vitaliy Ivashchenko

    Чтобы их обнаружить, метод main должен выглядеть примерно так:
    ThreadPoolExecutor executor = new ThreadPoolExecutor(
    3, 5, 10L,
    TimeUnit.SECONDS,
    new SynchronousQueue ()
    );

    executor.prestartAllCoreThreads();
    TestServer server = new TestServer(executor);

    ServerController serverController = new ServerController(server);
    MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
    ObjectName name = new ObjectName(“TestServer:name=sevcontrol”);
    mbs.registerMBean(serverController, name);

    server.runServer();

    Т.е. вызов server.runServer(); должен быть в самом конце.

    Сам уйму времени убил, чтобы понять в чем ошибка.

  9. msangel

    я просто оставлю здесь свое спасибо

Leave a Reply