JMX

A Unix/Linux szerverek üzemeltetése wikiből
(Változatok közti eltérés)
a
a
260. sor: 260. sor:
 
*Memőriaterületek (a heap és nem heap memória részei, java.lang:type=MemoryPool,name=terület neve): különböző lekérdező- és kezelő-metódusok.
 
*Memőriaterületek (a heap és nem heap memória részei, java.lang:type=MemoryPool,name=terület neve): különböző lekérdező- és kezelő-metódusok.
   
  +
Ezen MBean-ek egy része a Java platformot csak kevéssé ismerő felhasználók számára is értelmes jelentéssel bír - például ha a heap memória majdnem elfogyott és szinte folyamatosan fut a szemétgyűjtés, akkor "gond van"; más részük felügyeleti szempontból értelmes használatához (mint például a memóriaterületek) a Java platform behatóbb ismerete szükséges.
   
 
jconsole, VisualVM
 
jconsole, VisualVM

A lap 2008. december 19., 02:20-kori változata

A Java Managements Extensions (JMX) itt megtalálható tárgyalása szándékoltan annak rendszerfelügyeleti felhasználására koncentrál, az eszköz- és alkalmazásinstrumentáció rovására. A JMX "Java felügyelete Java-ból" jellege miatt az anyag feltételez legalább alapszintű Java programozási ismereteket az olvasó részéről; mindazonáltal a szerző reményei szerint ha nehezebben is, de érthető általános objektum orientált programozási és modellezési gyakorlattal is.

Tartalomjegyzék

1 Bevezető

A Java Management Extensions (JMX) egy, a Java SE 5.0-tól kezdve a platform-implementációk által kötelezően megvalósítandó szabvány. Célja mind a Java alkalmazások és a futtató platform (távoli) menedzsmentre felkészítésének, mind pedig a menedzsment célú hozzáférés mikéntjének szabványosítása.

A JMX mind menedzsment kiszolgálói (illetve menedzselt elem), mind pedig felügyeleti alkalmazás oldalon alapvetően a Java platform feletti implementációt feltételez; főként kötelezően megvalósítandó Java interfészeket, osztályokat és azok viselkedését írja le.

Hasonlóan például a Web Based Enterprise Management nyílt távfelügyeleti szabványhoz és az arra épülő megoldásokhoz, a JMX valódi objektum-orientált felügyeleti adatmodellezést tesz lehetővé (aminek nem csak az objektumok/asszociációk/attribútumok lekérdezése része, de metódusokat is lehet hívni); azzal ellentétben azonban nem definiál előre használható/kiterjeszthető modelleket. J2EE környezetekre a JMX-re épülő JSR-77 specifikáció határozza meg a felügyeleti modellt.

2 JMX alapfogalmak és architektúra

(thumbnail)
A JMX architektúrája (a szabvány alapján)
A JMX-ben a felügyelet szempontjából érdekes erőforrásokat reprezentáló illetve kezelő Java objektumok, un. managed Bean-ek (MBean-ek) egy kifejezetten felügyeleti célú névvel regisztrációra kerülnek egy MBeanServer-ben, attribútumaikkal és hívható metódusaikkal együtt. (A regisztrálhatósághoz bizonyos, pl. osztályelnevezési konvenciókat megadó feltételeket az objektumnak teljesítenie kell.)

Az MBeanServer egy, az MBean-eket nyilvántartó objektum, melynek publikus interfésze az MBean regisztráció és törlés mellett lehetőséget biztosít többek között:

  • MBean-ek név és név-minta szerinti keresésére,
  • MBean-ek attribútum- és metódushalmazának lekérdezésére,
  • attribútumok olvasására és írására,
  • metódusok hívására,
  • az MBean-ek által létrehozott jelzésekre (Notification-ök) feliratkozásra.

Megjegyzendő, hogy a mind lokális, mind távoli (lásd később) MBeanServer-hozzáférések esetén alkalmazható interfész metóduskészlete szűkebb, mint annak a specifikusan a lokális Java virtuális gépben található MBeanServer-hez kiterjesztett gyermeke.

A Java SE 5.0-tól kezdve a Java implementációk tartalmaznak egy platform MBeanServer-t. Ez amellett, hogy képes az alkalmazások felügyeletére létrehozott Mbean-eket kezelni, a java.lang.management API által meghatározott MBeaneket implementálva magának a futtató platformnak a felügyeletét is támogatja. Egy JVM-en belül több MBeanServer objektum létezése is megengedett; a platform mellett akár más JMX implementációkkal is dolgozhatunk egyidejűleg. (Az MBeanServer-ek alapesetben egymástól és egymás MBean-jeitől teljesen függetlenek; igény esetén azonban például kaszkádolhatóak.)

Az ugyanazon JVM-en belüli MBeanServer(-ek) elérésére a felügyeletet végző Java kód a specifikáció megvalósításainak egy factory osztályát, vagy a platform implementáció factory osztályát használhatja. A távoli eléréshez azonban nyilvánvalóan szükség van valamilyen mechanizmusra, amely egy JVM MBeanServer-eit valamilyen formában elérhetővé teszi egy másik JVM objektumai számára.

A specifikáció szerint ezt a funkcionalitást látják el a konnektorok. Egy konnektor-megvalósítás részei a konnektor kliens (connector client) és a konnektor kiszolgáló (connector server). A kliens oldali megvalósítás célja az, hogy olyan, a már említett távoli interfészt implementáló lokális objektumot hozzon létre, melynek lokális metódushívásai végrehajtódnak a távoli MBeanServer-en, illetve melynek jelzéseit az átadja a helyi előfizetőknek. A specifikáció kötelezően megvalósítandóként leír egy távoli metódushívás alapú (Java RMI) konnektort, illetve egy opcionálisan megvalósítandó "JMX Messaging Protocol" alapút.

A specifikáció elismeri "protokoll adapterek" (protocol adapters) implementálásának esetleges szükségességét; ezek nem-Java kliensek számára nyújtanának hozzáférést pl. HTML vagy SNMP felett. (A szerző legjobb tudomása szerint elterjedten használt ilyen megoldás nincs.) Egy nagyon érdekes, de 2008 októberében még csak "Early Access Draft" szintű projekt a Web Services Connector; a konnektor a WS-Management szabványt használja hordozóprotokollként, így a klienseknek nem kell mindenképpen Java alkalmazásoknak lenniük (az implementáció használhatóságát demonstrálták már WinRM-mel; elméletileg nyílt implementációkkal, mint pl. a wiseman is működőképesnek kell lennie.)

3 MBean-ek metaadatai

(thumbnail)
A JMX metaadat-osztályok (a szabvány 61. oldaláról átemelve); javax.management csomag
Altalános esetben egy MBean attribútumait és metódusait az MBeanServer-től kérdezzük le. Látható, hogy a metaadat-osztályok Java osztályok egyszerű leírását adják. Kiemelendő, hogy a típus-adatok (beépített Java típusok illetve osztálynevek) egyszerűen sztringként modellezettek. Elméletileg ez lehetőséget biztosít arra, hogy a kliens ellenőrizze, hogy rendelkezik-e a megfelelő osztálydefiníciókkal; a gyakorlatban azonban a hiányzó osztálydefiníciók és a sorosíthatósági problémák elkerülésére ajánlott az MBean-ek interfészében a beépített osztályokra és típusokra, valamint azok tömbjeire szorítkozni.

A Descriptor ("leíró") interfész segítségével a metaadat-osztályok példányaihoz (kulcs,érték) pár alakú bejegyzésekkel metaadatokat fűzhetünk, ahol a kulcs egy java.lang.String, az érték pedig egy általános java.lang.Object objektum. A specifikáció megadja kulcsok egy halmazát előre definiált névvel, céllal és lehetséges értékkészlettel (lásd az interfész definícióját). Ilyenek például a defaultValue kulcs (attribútum vagy metódus-paraméter alapértelmezett értéke, az érték objektum), a severity (jelzések súlyossága, értéke 0-tól 6-ig java.lang.Integer vagy java.lang.String) vagy a units kulcs (attribútum, metódus-paraméter vagy metódus visszatérési érték mértékegysége, például "bytes" vagy "seconds"; értéke java.lang.String típusú).

Példaként tekintsük a "java.lang:type=Runtime" nevű, magát a JVM-et reprezentáló platform MBean néhány, az MbeanServer-től programozottan lekérdezett attribútum metaadatát (a teljesség kedvéért: az MBeanInfo getMBeanInfo(ObjectName name) metódus segítségével). A leírók mezői itt azt adják meg, hogy hogyan lehet a Java típusokat egy, a szabvány által megadott egyszerű típuskészletre leképezni és ezzel elősegíteni, hogy az MBean nem Java kliensekkel is együtt tudjon működni (azaz hogy "open" MBean legyen). Ezen leírók gyakorlati jelentősége igen kicsi.

"Runtime" MBean attribute: BootClassPath
Description: BootClassPath
Attribute type: java.lang.String
Readable/writable: true/false
Attribute descriptor field openType: javax.management.openmbean.SimpleType(name=java.lang.String)
Attribute descriptor field originalType: java.lang.String

"Runtime" MBean attribute: Uptime
Description: Uptime
Attribute type: long
Readable/writable: true/false
Attribute descriptor field openType: javax.management.openmbean.SimpleType(name=java.lang.Long)
Attribute descriptor field originalType: long

"Runtime" MBean attribute: ClassPath
Description: ClassPath
Attribute type: java.lang.String
Readable/writable: true/false
Attribute descriptor field openType: javax.management.openmbean.SimpleType(name=java.lang.String)
Attribute descriptor field originalType: java.lang.String

4 Az MBean-ek elnevezése

Ismétlés képpen: az MBean-ek valódi Java objektumok, melyeket (általános esetben) nem közvetlenül érünk el (pontosabban fogalmazva: nincs rájuk objektum-referenciánk), hanem egy proxy jellegű Java objektumnak (az MBeanServer-nek) mondjuk meg, hogy melyik MBean-jével milyen operációt hajtasson végre, illetve azt kérdezzük le a benne regisztrált MBean-ekkel kapcsolatban. Ennek folyománya, hogy az MBeanServer meghívásai során valahogy meg kell jelölnünk azt az MBean-t, amelyikre például egy attribútum érték (vagy éppen attribútum készlet) lekérdezés vonatkozik - ezt viszont a szokásos módon, Java objektum-referenciákkal nem tudjuk megtenni. (Ez valójában inkább előny, mint hátrány...)

Az MBean-eket egy MBeanServer-ben az ún. objektum nevük azonosítja egyértelműen. API szempontból az objektum nevet a javax.management.ObjectName osztály reprezentálja. Egy objektum név két részből áll:

  • egy domain névből,
  • és egy vagy több kulcs tulajdonság (key property) rendezetlen halmazából.

A domain név egyszerűen egy sztring, mely az MBean-ek (jelenleg) nem hierarchikus névtér alapú elválasztását teszi lehetővé. A kettőspont (':'), csillag ('*') és kérdőjel ('?') karaktereken kívül minden karakter megengedett benne. Javasolt, hogy a domain név a kiadó szervezet DNS neve alapján, a Java csomagok elnevezési konvenciójához hasonlóan legyen felépítve (pl. hu.bme.mit.inf.MyDomain) - mindazonáltal a JMX implementáció ezt a hierarchiát nem értelmezi. Minden MBeanServer-nek van egy alapértelmezett domain-je; amennyiben domain név nélkül hivatkozunk MBean-ekre (vagy keresünk, pontos illeszkedést elvárva), úgy ezt használja.

A kulcs tulajdonságok kulcs-érték párok. Mind a kulcsok, mind az értékek sztringek; egy kulcs egy adott kulcs tulajdonság halmazban nem fordulhat elő kétszer. A type tulajdonság használata általánosan elterjedt; segítségével a nevekre való mintaillesztéssel lehet egyfajta primitív példányenumerációt megvalósítani. Bizonyos különleges karakterek (mint például a kettőspont) nem megengedettek sem a nevekben, sem az értékekben. Az objektum nevek szöveges reprezentációja a következő minta alapján történik: domainNév:kulcs=érték[,kulcs=érték]*. Egy szöveges reprezentáció kanonikus név, ha a névben a kulcsok lexikografikusan rendezettek. Néhány név példa (környezet: Sun JDK 1.6 felett a platform MBeanServer-t használó Apache Tomcat 5.5, a nevek a platform MBeanServer-ből származnak):

Catalina:j2eeType=Servlet,name=default,WebModule=//localhost/host-manager,J2EEApplication=none,J2EEServer=none
Catalina:type=Cache,host=localhost,path=/tomcat-docs
Catalina:type=Cache,host=localhost,path=/servlets-examples
Catalina:type=ThreadPool,name=jk-8009
java.lang:type=Runtime
java.util.logging:type=Logging
com.sun.management:type=HotSpotDiagnostic

Látható, hogy mind a platform, mind az alkalmazás MBean-ek esetében pusztán a név alapján nem igazán dönthető el, hogy az adott MBean mit modellez és mi a szerepe. Ezen az MBean-ek attribútumainak és operációinak lekérdezése sem valószínű hogy segíteni fog; az MBean-ek által megvalósított felügyeleti modellt mindenképpen meg kell ismerni a megfelelő dokumentációkból.

Egy objektum név lehet azonban minta is, melyre illeszkedő objektum neveket keresünk. Ekkor a '*' és a '?' karakterek használhatók mint wildcard-ok (a szokásos módon; * akármilyen hosszú karaktersorozat, ? pontosan egy karakter).

Egy másik, az objektumnévhez hasonlóan alapvető fontosságú osztály az ObjectInstance (objektum példány - vitatható hogy ennek a szóösszetételnek van-e értelme). Példányai objektumnév - osztálynév összerendeléseket tartalmaznak, azaz megadják, hogy az adott néven MBean-ként regisztrált Java objektum mely Java osztály példánya.

5 A JVM felkészítése

Bár a platform MBeanServer implementációként megtalálható bármely Java SE 5 vagy 6 Java implementációban, ez még nem jelenti automatikusan azt, hogy bármely futó JVM megkötések nélkül felügyelhető lenne másik JVM-ből (adott JVM-en belül alapértelmezetten elérhetjük a JVM platform MBeanServer-ét). A platform MBeanServer beépített RMI alapú konnektorára vagy csak helyi, vagy helyi és távoli hozzáférést állíthatunk be.

A helyi hozzáférés a Java SE 5-ben jelent meg, engedélyezése a com.sun.management.jmxremote VM kapcsolóval lehetséges:

java -Dcom.sun.management.jmxremote DummyServer

A JSE 5 esetén azonban az így konfigurált VM-ben létrejövő konnektor programozottan nem volt elérhető, csak a Sun (illetve az IBM) által szállított JConsole grafikus eszköz volt képes ahhoz a fenti módon való engedélyezés után csatlakozni.

Ez a Java SE 6 kiadásával megváltozott. A nem szabványos, de a Sun által támogatott Attach API lehetővé teszi, hogy egy JVM "rácsatlakozzon" az őt futtató felhasználó egy másik JVM folyamatára. Ezzel lehetőség a nyílik a fenti kapcsoló hatására létrejött konnektor cím kiolvasására és az alapján a csatolt JVM platform MBeanServer-éhez csatlakozásra. Az Attach API viszont arra is módot ad, hogy ellenőrizzük, hogy létezik-e már a helyi konnektor cím és ha nem, akkor létrehozzuk az (egy egyszerű példa itt). Ezzel a 6-os verzióban a kapcsoló tovább él, de meglehetősen okafogyottá vált. A 6-os JDK beépített felügyeleti eszközei (a JConsole és a VisualVM; lásd később) képesek így lokális JVM-ekre csatlakozni. Megjegyzendő, hogy mivel lokális kapcsolat jön létre ugyanazon felhasználó két folyamata között, további jogosultságellenőrzések nem történnek.

A távoli hozzáférést (és mellékhatásként egyben a helyit is) a com.sun.management.jmxremote.port kapcsolóval engedélyezhetjük, megadva a portot amelyen az RMI konnektor kiszolgáló a kapcsolatokat várja. További kapcsolókkal rendelkezhetünk a kapcsolat titkosításáról és az autentikáció módjáról is:

java -Dcom.sun.management.jmxremote.port=9004 \
-Dcom.sun.management.jmxremote.ssl=false \
-Dcom.sun.management.jmxremote.authenticate=false \
DummyServer

FIGYELEM: a fenti példát a külvilágtól megfelelően leválasztott tesztkörnyezeteket leszámítva ne használjuk, hiszen ez esetben se titkosítás nincs, se (legalább jelszó alapú) autentikáció. Az adatbiztonságot érintő lehetőségekkel és beállításokkal kapcsolatban lásd a megfelelő Sun oldalt.

6 Programozott távoli hozzáférés: egy példaprogram

A Java-ban megvalósított programozott távoli hozzáférés szemléltetésére bemutatunk egy egyszerű példaprogramot, mely egy, a helyi gépen futó, távoli hozzáférésre felkonfigurált JVM platform MBeanServer-éhez csatlakozik. A kód kimérete (a Java nem éppen feszes szintaxisának és programozási stílusának köszönhetően) megtévesztő; a kapcsolódás és az operációk egy igen egyszerű logikát követnek. A használt osztályokat és metódusokat itt részletesen nem tárgyaljuk; az érdeklődő olvasó azokat megtalálja a javax.management csomag dokumentációjában.

Első lépésként egy speciális URL objektumban megadjuk a JMX szolgáltatás (pontosabban az RMI konnektor kiszolgáló) címét, létrehozunk egy konnektor-objektumot erre a címre, és megpróbálunk tőle elkérni egy (már elindított) kapcsolat objektumot, mely az MBeanServer már korábban említett, távoli hozzáférésre szűkített interfészét implementálja:

import java.io.IOException;
import java.net.MalformedURLException;
import java.util.Iterator;
import java.util.Set;

import javax.management.*;

public class MyClient {

	public static void main(String[] args){
	
		
		JMXServiceURL address = null;
		JMXConnector connector = null;
		MBeanServerConnection connection = null;
		try {
			address = new JMXServiceURL("service:jmx:rmi:///jndi/rmi://localhost:9004/jmxrmi");
		} catch (MalformedURLException e) {
			e.printStackTrace(); System.exit(1);
		}
		
		try {
			//Note: for real life cases it's not that simple; we would have to
			//specify a host of additional information in a further java.util.Map
			//argument.
			connector = JMXConnectorFactory.connect(address);
		} catch (IOException e) {
			e.printStackTrace(); System.exit(1);
		}
	
		try {
			connection = connector.getMBeanServerConnection();
		} catch (IOException e) {
			e.printStackTrace(); System.exit(1);
		}

Ezek után "körbenézünk": lekérdezzük és kiírjuk a domain-eket, az alapértelmezett domain-t, az MBean-ek számát és nevét:

		try {
			String[] domains = connection.getDomains();
			System.out.println("Domains:");
			System.out.println("-----------------------");
			for(int i = 0; i < domains.length; i++){
				System.out.println(domains[i]);
			}
			System.out.println();
			
			System.out.println("Default domain: " + connection.getDefaultDomain());
			System.out.println("MBean count: " + connection.getMBeanCount() + "\n");
			
			System.out.println("MBean names:");
			System.out.println("-----------------------");
			Set<ObjectName> names = connection.queryNames(null, null);
			Iterator<ObjectName> si = names.iterator();
			while(si.hasNext()){
				ObjectName name = si.next();
				System.out.println(name);
			}

A már említett Tomcat 5.5 környezetre a kód konzol-kimenetének eleje a következő (a Tomcat MBean-jeinek nagy száma miatt a lista erősen rövidített):

Domains:
-----------------------
JMImplementation
Users
com.sun.management
Catalina
java.lang
java.util.logging

Default domain: DefaultDomain
MBean count: 212

MBean names:
-----------------------
Catalina:j2eeType=WebModule,name=//localhost/host-manager,J2EEApplication=none,J2EEServer=none
Catalina:type=ServerClassLoader,name=shared
Catalina:j2eeType=Servlet,name=default,WebModule=//localhost/webdav,J2EEApplication=none,J2EEServer=none
...
java.lang:type=Memory
...
java.lang:type=Compilation
...
java.lang:type=Runtime
...

Tegyük fel, hogy már tudjuk hogy a java.lang:type=Memory MBean-nek milyen attribútamai vannak és mi valójában a folyamat által aktuálisan használt heap memória mennyiségére vagyunk kíváncsiak:


			try {
				javax.management.openmbean.CompositeDataSupport mUsage = 
					(javax.management.openmbean.CompositeDataSupport)
					connection.getAttribute(new ObjectName("java.lang:type=Memory"), 
							"HeapMemoryUsage");
				Long heap = (Long)mUsage.get("used");
				System.out.println(heap);
			} catch (Exception e) {
				e.printStackTrace(); System.exit(1);
			}

Eredményként megkapjuk, hogy jelenleg megközelítőleg 5MB heap memória van használatban:

5090544

A programot azzal fejezzük be hogy lezárjuk a kapcsolatot:

		} catch (IOException e) {
			e.printStackTrace(); System.exit(1);
		}
		
		try {
			connector.close();
		} catch (IOException e) {
			e.printStackTrace(); System.exit(1);
		}
	}	
}

7 A platform MBean-ek

Ha egy Java alkalmazás a távfelügyeletére nem is regisztrál MBean-eket, a JMX akkor is hasznos lehet: a platform MBean-ek segítségével fontos információkhoz juthatunk a JVM-mel kapcsolatban, illetve befolyásolhatjuk annak működését.

  • Osztálybetöltés (java.lang:type=ClassLoading): betöltött/kiürített osztályok számának lekérdezése
  • Fordítás (java.lang:type=Compilation): a "Just In Time" (JIT) fordító nevének és a fordítással eltöltött időnek a lekérdezése
  • Memória (java.lang:type=Memory): összes rendelkezésre álló, lefoglalt és aktuálisan használt heap és nem heap memória mennyiségének lekérdezése, szemétgyűjtés (garbage collection) indítása.
  • Szálkezelés (java.lang:type=type=Threading): szálak számának, CPU használatának és egyéb jellemzőinek lekérdezése.
  • Futtatórendszer (java.lang:type=Runtime): a virtuális gép tulajdonságainak, mint például gyártó, a megvalósított Java specifikáció vagy a classpath lekérdezése.
  • Operációs rendszer (java.lang:type=OperatingSystem): operációs rendszer nevének, verziójának, az architektúrának és a processzorok számának a lekérdezése.
  • Szemétgyűjtők (java.lang:type=GarbageCollector,name=szemétgyűjtő neve): statisztikai adatok a szemétgyűjtőkről.
  • Memóriakezelők (java.lang:type=MemoryManager,name=memóriakezelő neve): a memóriakezelők alapadatai.
  • Memőriaterületek (a heap és nem heap memória részei, java.lang:type=MemoryPool,name=terület neve): különböző lekérdező- és kezelő-metódusok.

Ezen MBean-ek egy része a Java platformot csak kevéssé ismerő felhasználók számára is értelmes jelentéssel bír - például ha a heap memória majdnem elfogyott és szinte folyamatosan fut a szemétgyűjtés, akkor "gond van"; más részük felügyeleti szempontból értelmes használatához (mint például a memóriaterületek) a Java platform behatóbb ismerete szükséges.

jconsole, VisualVM

Kitekintés: egyéb Java eszközök "Monitor and diagnose performance in Java SE 6" alapján

Ajánlott irodalom: Java SE Monitoring and Management Guide Kimaradt: ágens szolgáltatások

Személyes eszközök