Java monitorozás és menedzsment

Technológiák: Servlet 3.0, JMX

Sajnos fejlesztés közben viszonylag kevés figyelmet fordítunk arra, hogy könnyen üzemeltethető alkalmazásokat készítsünk. Pedig a Java technológia lehetőséget biztosít, csak kicsit jobban oda kell figyelnünk, kicsit jobban ki kell használni az eszközöket és az API-kat.

Hogy erre felhívjam a figyelmet, 2009. szeptember 16-án a SZÁMALK Aktuális 2009 rendezvényén előadást tartottam "Hol a határ? - Java alkalmazások üzemeltetéséről fejlesztőknek és üzemeltetőknek" címmel.

Az előadás során végigvettem egy fejlesztési életciklust, valamint egy tipikus n-rétegű alkalmazás architektúrát, és elemeztem a fejlesztők és az üzemeltetők feladatait, valamint a lehetséges konfliktus forrásokat.

Konklúzióként levonható, hogy a technológia már nagyon jó eszközöket ad a kezünkbe, a probléma mindig emberi oldalon szokott jelentkezni.

Java szempontjából talán a következőket érdemes kiemelni:

  • Vastag kliens esetén a telepítés és a frissítések kezelésére érdemes a Java Web Start technológiát használni, mely alapban a JRE része.
  • Hasznos eszköz a JConsole, mely a JDK része, és a futó Java alkalmazásokhoz képes hozzákapcsolódni, és azok állapotát lekérdezni.
  • Hasznos API a Java Management Extensions (JMX), mely használatával könnyen üzemeltethető alkalmazásokat tudunk készíteni.

A JMX a Java SE 5.0-tól a platform része, olyan szabványos programozói interfész (JSR 3), melyel monitorozható és menedzselhető alkalmazásokat tudunk készíteni. A JMX alapját egy vagy több Java objektum, un. managed bean (MBean) képviseli. Az MBean-eket az MBean szerverbe kell regisztrálni, hogy a kliensek el tudják érni. Egy MBean-nek lehetnek attribútumai, melyeket lehet írni és/vagy olvasni, lehetnek műveletek (operations), melyeket meg lehet hívni, valamint bizonyos értesítéseket küldhetnek. Ezáltal az MBean-eken keresztül megfigyelhető egy alkalmazás állapota, közbe lehet avatkozni, és bizonyos eseményekről is értesítést kaphat az üzemeltető. Az MBean-ek lokálisan, de távolról is elérhetőek (JSR 160, Java Management Extensions Remote API).

Amennyiben elindítunk egy Java programot, és elindítjuk a JConsole alkalmazást, információt kaphatunk a memóriafogyasztásról, futó szálakról, betöltött osztályokról, stb. Az utolsó, MBeans nevezetű fülön jelennek meg az MBean-ek. Látható, hogy minden konfiguráció nélkül is van pár MBean, melyek a JVM-ről adnak információkat (memóriahasználat, szemétgyűjtő, osztálybetöltő, operációs rendszer környezet, stb.), valamint a JVM működésébe lehet beavatkozni (pl. java.lang/Memory - gc művelet).

A legtöbb alkalmazásszerver menedzsmentje is a JMX-re épül. Ilyen a Tomcat is, mely szintén JMX-en biztosítja a monitorozást és a menedzsmentet. Ekkor amint csatlakozunk a JConsole-lal a Tomcat-et futtató JVM-hez, az MBeans fülön megjelenik a Catalina és a Users folder. Ezekben rengeteg MBean-t találhatunk. A Tomcat olyan szinten biztosít információkat, mint pl. egy servlet betöltési ideje, meghívásának száma, legkisebb és legnagyobb lefutási idő, összes idő, mennyit hibázott, stb (Catalina/Servlet folder).

Nézzünk is egy példát, írjunk egy egyszerű webes alkalmazást. Letölthető a https://github.com/vicziani/jtechlog-jmx címről. Jettyn is megy, Maven-nel build-elhető, és a letöltést követően a 'mvn jetty:run' paranccsal futtatható. Az alkalmazás egy servletből áll, mely egy számlálót növel minden egyes meghívásakor. Ezt szeretnénk kiajánlani JMX-en. A számlálóhoz készítsünk egy külön osztályt Counter néven.

public class Counter
    implements CounterMBean {

private long value;

public long getValue() {
    return value;
}

public void setValue(long value) {
    this.value = value;
}

public void storno() {
    value = 0;
}

synchronized public void incrementCounter() {
    value++;
}
}

Ennek incrementCounter metódusát hívja a szerver. Ahogy látható, implementálja a CounterMBean interfészt, melyen keresztül a JMX-en ki lesz ajánlva.

public interface CounterMBean {
public long getValue();
public void setValue(long counter);
public void storno();
}

Eztán már csak egy ServletContextListener-t kell implementálni, mely az induláskor regisztrálja az MBean-t, leálláskor meg megszünteti a regisztrációt.

@WebListener
public class InitServletListener implements ServletContextListener {
public void contextInitialized(ServletContextEvent sce) {
    try {
        MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
        CounterMBean counter = new Counter();
        mbs.registerMBean(counter, 
    new ObjectName("jtechlog:type=Counter"));
    
        sce.getServletContext().setAttribute("counter", counter);
    } catch (Exception e) {
        e.printStackTrace();
    }
}

public void contextDestroyed(ServletContextEvent sce) {
    try {
     MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
     mbs.unregisterMBean(new ObjectName("jtechlog:type=Counter"));
    } catch (Exception e) {
        e.printStackTrace();
    }
}
}

A web alkalmazást, majd a JConsole-t elindítva láthatjuk, hogy megjelent a jtechlog folder, és azon belül a Conter MBean. Lekérdezhetjük vagy beállíthatjuk a value értékét, vagy meghívhatjuk a storno műveletet.

Amennyiben értesítést is szeretnénk kapni a számláló értékének változásáról, a NotificationBroadcasterSupport osztályból kell származtatni, implementálni kell a getNotificationInfo metódust, majd meghívni a sendNotification metódust.

...
synchronized public void incrementCounter() {
    value++;
    Notification n =
        new AttributeChangeNotification(this,
        sequenceNumber++,
        System.currentTimeMillis(),
        "Counter value has changed",
        "Counter value",
        "long",
        value - 1,
        value);

        sendNotification(n);
}

@Override
public MBeanNotificationInfo[] getNotificationInfo() {
    String[] types = new String[]{
        AttributeChangeNotification.ATTRIBUTE_CHANGE
    };
    String name = AttributeChangeNotification.class.getName();
    String description = "An attribute of this MBean has changed";
    MBeanNotificationInfo info =
            new MBeanNotificationInfo(types, name, description);
    return new MBeanNotificationInfo[]{info};
}
...

A legjobb, hogy ezeket az értékeket nem csak JConsole-ról tudjuk lekérdezni, hanem parancssorból is, a Tomcat Ant task-okat definiál erre. (Használatához a catalina-ant.jar-t kell a $CATALINA_HOME/lib könyvtárból az $ANT_HOME/lib könyvtárba másolni.) A következő build.xml részlettel lehet lekérni a számláló értékét.

<jmx:open
host="${jmx.server.name}"
port="${jmx.server.port}"/>
<jmx:get
name="jtechlog:type=Counter"
attribute="Value"
resultproperty="value"
echo="false"
/>
<echo message="${value}" />

Ez azért nagyszerű, mert így bármilyen monitorozó vagy menedzsment eszközbe (pl. Munin, Nagios) könnyen be tudjuk kötni.