JavaSvet - otvorena java zajednica

 
glavna stranica arr2javasvet  english version arr2java.net

Java management extensions (JMX)

Saša Slavnić
Sadržaj:
13 Okt 2009

Koliko sam u dosadašnjem iskustvu primetio, upravljivosti razvijenih aplikacija se obično poklanja malo ili najčešće nimalo pažnje. Veoma malo programera ili projektanata može da odgovori na sasvim jednostavna pitanja o projektima koje su sami napravili, kao na primer:

 

Osim toga često se upravljanje aplikacijom svodi na periodični restart kompletnog servera u slučaju da nešto ne radi. Niko se i ne pita šta to tačno ne radi, ni da li se može nekako bezbolnije popraviti.

Šta bi bilo kad bi imali neki alat pomoću kog bi mogli zaviriti unutar aplikacije, i pogledati trenutne vrednosti objekata? I kad bi mogli u sred izvršavanja aplikacije promeniti vrednost tih promenjivih? Ili još bolje, "ručno" pozvati bilo koji metod u aplikaciji, u trenutku dok se ona izvršava? Odgovor je kao i uvek troslovna skraćenica, a ovaj put je to JMX.

Šta je JMX?

JMX je skraćenica za Java management extensions. Iza naziva se opet krije način da upravljate aplikacijama ili servisima. JMX definiše arhitekturu, API i Java servise za upravljanje aplikacijama. Tehnologija se u principu sastoji od tri koraka:

JMX arhitektura pojednostavljeno izgleda kao na slici:

JMX arhitektura

Kao dodatni bonus, korišćenje JMX standarda vam omogućava da upravljate i mnogim tehnologijama koje koristite u svakodnevnom radu. Zaista mnogo prvoklasnih biblioteka (na primer ActiveMQ) su instrumentalizovane kroz JMX tako da njihov monitoring i podešavanje dobijate bez ikakvog truda. Osim toga i sama virtuelna mašina je na isti način instrumentalizovana, pa možete na primer gledati koliko JVM memorije ili niti alocira. Posebno moćan osećaj je mogućnost da kliknete na GC collect dugme – garantovano ćete se osećati kao Java guru. :)

Primer jednostavnog MBeana

Evo jednog jednostavnog primera na kom se vidi koliko je u stvari trivijalno koristiti JMX ili ga uvesti u postojeću aplikaciju. U ovom primeru se radi o konzolnoj aplikaciji, ali vas ništa ne sprečava da isti princip koristite u bilo kakvom servisu, swing ili web aplikaciji. Primer će eksportovati atribute i operacije jednog objekta koji kasnije možemo pogledati u JConsole monitoru. Nadgledaćemo vrednost polja counter, i moći ćemo da izvršimo operacije pause, resume i incrementCounter.

Svakako, u realnom okruženju ništa vas ne sprečava da napravite proizvoljno kompleksne objekte koje kasnije možete nadgledati. Na primer, zašto ne napraviti MBean u kom bi mogli da podešavate maksimalan broj konekcija na server, da praznite keš, gledate trenutno ulogovane korisnike ili pratite brzinu izvršavanja pojedinih operacija u aplikaciji.

Za početak, deklarisaćemo interfejs objekta koji želimo nadgledati. Po konvenciji (koja se obavezno mora pratiti), ime interfejsa se završava sa „Mbean“.

public interface WorkerMBean {
  
  public long getCounter();
  public void setCounter(long counter);
  public void incrementCounter();
  
  public void pause();
  public void resume();
  
}

Implementacija je takođe jednostavna. Po konvenciji, klasa koja implementira MBean interfejs se zove kao interfejs, ali bez nastavka.

public class Worker implements WorkerMBean {

  private long counter = 0;
  
  private boolean pause = false;
  
  @Override
  public long getCounter() {
    return counter;
  }

  @Override
  public void pause() {
    pause = true;
  }
  
  @Override
  public void resume() {
    pause = false;
  }

  @Override
  public void setCounter(long counter) {
    if (pausereturn;
    this.counter = counter;
    System.out.println("Counting: " + counter);
  }
  
  public void incrementCounter() {  
    setCounter(counter + 1);
  }
}

Main metod instancira Worker klasu, i na svaki sekund inkrementira brojač. Ovako bi izgledao naš test program bez korišćenja JMXa:

public class Main {

  public static void main(String[] args) {

    Worker worker = new Worker();

    while (true) {
      try {
        Thread.sleep(1000);
        worker.incrementCounter();
      catch (InterruptedException e) {
        e.printStackTrace();
      }
    }
  }
}

Da bismo našu instancu videli kroz JMX, potrebno je da se ona registruje u MBeans serveru. Kod za to je veoma jednostavan. Prvo dobavimo instancu MBeans servera pozivom:

MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();

I zatim registrujemo naš objekat:

ObjectName name = new ObjectName("net.javasvet:type=Worker");
mbs.registerMBean(worker, name);

Tako registrovan objekat kasnije je moguće videti u bilo kom JMX monitoring alatu. Kompletna Main klasa našeg primera na kraju izgleda ovako:

public class Main {

  public static void main(String[] args) {

    MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
    Worker worker = new Worker();
    
    try {
      ObjectName name = new ObjectName("net.javasvet:type=Worker");
      mbs.registerMBean(worker, name);
    catch (Exception e) {
      e.printStackTrace();
    }

    while (true) {
      try {
        Thread.sleep(1000);
        worker.incrementCounter();
      catch (InterruptedException e) {
        e.printStackTrace();
      }
    }
  }
}

JConsole

Posle pokretanja našeg test programa, možemo pogledati kako to izgleda iz JConsole. JConsole nalazi u "bin" folderu JDK instalacije. Na početku se prikazuje ekran u kom se vide sve virtuelne mašine na računaru, i gde možemo izabrati koju JVM želimo da posmatramo.

JConsole - uvodni ekran

Nakon izbora virtuelne mašine otvara se prozor u kom možemo videti detaljnije informacije. Sama virtuelna mašina je u velikoj meri instrumentalizovana, tako da je moguće pratiti informacije o stanju same VM. Moguće je videti detalje konfiguracije, raspoloživu i zauzetu memoriju, informacije o klasama i nitima, te podesiti parametre virtuelne mašine.

JConsole - pregled memorije

Ono što nas zanima je jezičak MBeans na kom se mogu videti svi registrovani beanovi. Između ostalog tu se pojavljuje i WorkerMBean:

JConsole - pregled memorije

Kao što se vidi na slikama, moguće je videti vrednost promenjivih unutar beana, te pritiskom na dugme pozvati određeni metod (da, toliko je jednostavno). Na slici se takođe vide eksportovani objekti od drugih komponenti – monitoring i upravljanje tim objektima je bonus koji dobijate bez ijedne linije koda ili bilo kakvog truda za instalaciju.

Primer - Springframework

Naravno, sve što je jednostavno uraditi u Javi, uz pomoć Springa je još jednostavnije. Da bi koristili MBeans, nije potrebno deklarisati interfejs. Osim toga o registraciji MBeana se brine Spring, tako da je sve što je potrebno je deklarisati eksporter u konfiguracionom fajlu:

<bean id="exporter" 
    class="org.springframework.jmx.export.MBeanExporter" lazy-init="false">
  <property name="beans">
    <map>
      <entry key="net.javasvet:name=worker" value-ref="worker"/>
    </map>
  </property>
</bean>

<bean id="worker" class="net.javasvet.Worker" />

Main klasa sad izgleda ovako:

public class Main {
    public static void main(String[] args) {
        ClassPathXmlApplicationContext context ;
        String location = new String[]{"net/javasvet/AppConfig.xml"};
        context = new ClassPathXmlApplicationContext(location);
        Worker worker = (Worker)context.getBean("worker");

        while (true) {
            try {
                Thread.sleep(1000);
                worker.incrementCounter();
            catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

Primer – Groovy

Za sve mnogobrojne fanove Groovyja ;) evo i primer kako se tamo koristi JMX. Groovy ne bi bio to što jeste da ne postoji builder za JMX. Uz njegovu pomoć eksportovanje MBeana je nešto lakše. Nije potrebno definisati interfejs, a pored toga builder na sebe preuzima zadatak registrovanja.

import groovy.jmx.builder.JmxBuilder

public class Worker {

    long counter = 0
    boolean pause = false
    
    public void pause() {
        pause = true
    }
    
    public void resume() {
        pause = false
    }

    public void setCounter(counter) {
        if (pausereturn
        this.counter = counter
        println "Counting: " + counter
    }
    
    public void incrementCounter() {    
        setCounter(counter + 1)
    }
}

def worker = new Worker()
def jmx = new JmxBuilder()

jmx.export {
    bean(worker)
}

while (true) {
    Thread.sleep(1000)
    worker.incrementCounter()
}

Više informacija o JmxBuilderu se može naći na http://groovy.codehaus.org/Groovy+JmxBuilder

Šta još?

Ono što je takođe moguće postići (a nije pokriveno ovim člankom) su notifikacije, odnosno MBeans mogu poslati informaciju o nekom događaju koja se posle može pratiti u alatu za monitoring. Ukoliko je neko zainteresovan dodatne informacije može pronaći na http://java.sun.com/docs/books/tutorial/jmx/notifs/index.html

Zaključak

Ja u Java tehnologiji nikad nisam video bolje osmišljenu ili implementiranu tehnologiju. Stvar se jednostavno koristi, i prosto rečeno – radi. Sama mogućnost da recimo možete da povećate broj DB konekcija bez da prekinete rad aplikacije je fascinantna. Ili da recimo pauzirate neki zahtevan proces i pokrenete ga kad je opterećenje sistema manje. Pošto se JMX tako jednostavno koristi, šteta je bar ne probati kako stvar radi. A za bilo koji sistem koji iole cilja na high-availability, JMX se jednostavno mora primenjivati. Ukratko – sve preporuke.