Wenn nicht erst die Beschwerden der Anwender auf Probleme mit einer Java-Anwendung hinweisen sollen, muss es möglich sein, die Server und Anwendungen aktiv zu überwachen und während der Laufzeit eventuell umzukonfigurieren. Mit Java-MBeans ist dies kein Problem.
Während der Anwendungsentwicklung gerät das Systemmanagement leider allzu oft in Vergessenheit. Das Ergebnis: Hängt eine Anwendung, bekommt es keiner mit. Der Unterschied zwischen einer hängenden Applikation und einer, die läuft, ist nicht erkennbar. Server-basierte Programme sind hier besonders betroffen, denn die Ursachen sind schwer zu finden (Netzwerker kennen das: Im Zweifelsfall ist das Netzwerk schuld) und sie treffen immer viele Anwender. Desktop-Anwendungen sind eher unproblematisch, hier helfen notfalls ein beherztes Kill und ein Neustart.
Systemmanagement nicht nur für den Ernstfall
Aber auch wenn es gar nicht zu Problemen kommt, sind Informationen über laufende Programme wichtig. Benutzt ein Applikationsserver etwa einen Connection-Pool für die Datenbank, dann sind Antwortzeit-Verhalten und Ressourcenbedarf abhängig von der Auslastung des Pools. Zu wenige Connections bedeuten lange Wartezeiten, zu viele einen unnötig hohen Ressourcenverbrauch. Das Monitoring und die Umkonfiguration im laufenden Betrieb der Anwendung sind Kerneigenschaften jeder guten Serveranwendung.
Typischerweise gibt es für Serveranwendungen deshalb eine Managementkonsole, die zur Konfiguration und Überwachung dient. Abbildung 1 zeigt das Prinzip: Während die normalen Clients über einen offiziellen Port auf den Server zugreifen, gibt es für das Management einen eigenen Port. Alternativ gibt es einen geschützten Einstieg in das Managementmodul über den normalen Zugang.
Modern ist Web-basiertes Management; hier enthält die Anwendung einen eingebauten Webserver. Eine Anwendung kann auch ein Gerät sein, viele Netzwerkdrucker erlauben zum Beispiel die Konfiguration und Statusabfrage über einen Webserver – oft völlig ungeschützt.
Die JMX-Architektur
Um das Rad nicht für jede (Java-)Anwendung neu zu erfinden, gibt es für das Management von Java-Anwendungen einen Standard: die Java Management Extensions, kurz JMX. Kernelemente dieses Standards sind die Managed Beans, kurz MBeans. Diese Folge des Coffee-Shops erläutert die Architektur und zeigt anhand eines Beispiels, wie einfach eine Anwendung JMX-fähig gemacht werden kann.
Die JMX-Architektur unterscheidet sich nicht prinzipiell von der oben skizzierten Struktur. Auf der einen Seite steht die Managementkonsole, auf der anderen die JMX-fähige Anwendung. Insgesamt kann man sechs (logische) Komponenten unterscheiden (siehe auch Abbildung 2):
- Die eigentliche Anwendung. Sie enthält die Parameter (allgemeiner: die Ressourcen), die abgefragt (Monitoring) oder gesetzt und manipuliert werden können (Umkonfiguration). Typischerweise gibt es dafür Klassen mit Getter/Setter-Methoden.
- Die MBeans. Sie instrumentieren die Ressourcen der Anwendung und machen sie somit durch JMX verwaltbar. Es sind Java-Klassen, die entweder einem festgelegten Design-Pattern folgen oder ein definiertes Interface implementieren. Der Kasten “MBeans” erläutert die Details. MBeans sind die Brücke zwischen dem MBeans-Server und der Anwendung (Instrumentation Level).
- Der MBeans-Server. Dieser Teil der Anwendung verwaltet die MBeans und bildet zusammen mit den Agent- Services den Agent-Level.
- Die Agent-Services sind Objekte, die Managementoperationen an MBeans durchführen können. Agent-Services sind oft selbst als MBeans implementiert. Die Spezifikation definiert in der Version 1.2 vier Services: Dynamic Class Loading, Monitoring, Timer und den Relation-Service.
- Ein oder mehrere Adapter und Konnektoren. Sie dienen der Interaktion von anderen Applikationen mit dem Agent-Level. Ein HTML-Adapter zum Beispiel stellt einen minimalen Webserver zur Verfügung, über den die MBeans angesprochen werden können. Auch Adapter können selbst als MBeans implementiert sein.
- Die Management-Applikationen, im einfachsten Fall ist das ein Webbrowser. Diese Schicht bildet mit den Adaptern und Konnektoren den Distributed-Services-Level und ist zurzeit noch nicht spezifiziert.
Die ersten fünf Komponenten sind Teil derselben Anwendung und laufen in derselben JVM. Je nach Anwendungsarchitektur und Philosophie gibt es auch einen fließenden Übergang zwischen Anwendung und MBeans. Die Architektur und das Zusammenspiel der Komponenten wird später anhand eines Beispiels schnell klar.
Der MBeans-Server verwaltet die MBeans. Jede JMX-Implementation stellt zu diesem Zweck eine Factory-Methode bereit (»MBeanServerFactory.createMBeanServer()«), die einen Server erzeugt. Mit der »MBeanServer«-Instanz erzeugt man die MBeans (»server.createMBean()«), führt Methoden aus (»server.invoke()«) und setzt oder liest Attribute (»server.setAttribute()« und »server.getAttribute()«).
MBeans im Griff
Bei der Erzeugung von MBeans erhalten diese auch Namen. Das erlaubt es Managementanwendungen, die MBeans darüber anzusprechen, ohne dass Objektreferenzen ins Spiel kommen. Namen folgen dem Schema:
[domain:]key1=value1[,key2=value2...]
Die Domain ist optional, stellt aber bei geschickter Wahl (etwa dem voll qualifizierten Klassennamen der Anwendung) sicher, dass es zu keinen Überschneidungen kommt. MBean-Namen sind in speziellen Objekten der Klasse »ObjectName« gekapselt.
Im Grunde genommen hat ein Anwendungsprogrammierer mit dem MBean-Server wenig zu tun. Anstatt die oben genannten dynamischen Methoden zu nutzen, wird er die MBeans erzeugen, konfigurieren und einfach mit »server.registerMBean()« dem Server bekannt machen. Weiter gehende Manipulationen an den MBeans erfolgen dann über ein Management-Interface.
Download und Installation
Sun stellt unter[1] eine Referenzimplementation der Spezifikation (in der Version 1.2) zur Verfügung. Es steht sowohl ein Paket mit den Quellen als auch eine Binärversion bereit. Für eine JMX-Zertifizierung, die die Quellen der Referenzimplementierung verwendet, werden allerdings Lizenzgebühren fällig. Das Sun-Binärpaket ist eine 1,6 MByte große Zip-Datei ohne Top-Level-verzeichnis. Es gibt aber auch eine Open-Source-Implementation der JMX-Spezifikation, sie heißt »mx4j«. Auf Sourceforge[2] gehostet, implementiert dieses Projekt momentan die ältere Version 1.1 der Spezifikation.
Mxj4 kann man als komprimiertes Tar-Archiv herunterladen (1,8 MByte groß) und an einer beliebigen Stelle entpacken (es enthält schon ein Top-Level-Verzeichnis). Mit dem Entpacken ist alles getan, eine weiter gehende Konfiguration ist in beiden Fällen nicht notwendig. Wer Zusatzfunktionen von Mx4j nutzen will, braucht aber zusätzliche Pakete. Insbesondere das bekannte »log4j« für Tracing und Logging erweist sich als nützlich. Das Readme nennt die entsprechenden Quellen.
Die JMX-Spezifikation, unter dem Java-Community-Process entwickelt, gibt es auf[3]. Sie ist zwar für die Nutzung nicht notwendig, bietet aber als letzte Referenz einen guten Einblick in das Design und den Leistungsumfang sowie in die noch offenen Punkte, die in späteren Versionen für die Standardisierung vorgesehen sind.
Server-Monitoring als Beispiel
Das Serverprogramm des Beispiels liest ähnlich wie »top(1)« Leistungsparameter des Systems aus: die CPU-Nutzung sowie die Speichernutzung. In Listing 1 ist das Programm »LinuxInfo.java« stark gekürzt abgedruckt, komplett ist es unter[4] zu finden.
Zwei MBeans dienen dem Monitoring: »Cpu« und »Mem«. Diese implementieren die jeweiligen MBean-Interfaces »CpuMBean« und »MemMBean« (Listings 2 und 3). Die Implementationen greifen auf entsprechende Methoden der »LinuxInfo«-Klasse durch (Zeilen 80 bis 82 sowie 125 bis 132 in Listing 1), enthalten also keine eigene Logik.
Eine weitere MBean »UpdateInterval« mit dem Interface »UpdateIntervalMBean« legt das Sampling-Intervall in Millisekunden fest (Listing 4). Im Gegensatz zu »Cpu« und »Mem« erlaubt »UpdateInterval« sowohl lesenden als auch schreibenden Zugriff.
Die »setupAgent()«-Methode von »LinuxInfo« (Zeilen 178 bis 202, aufgerufen aus der »main«-Methode, Zeile 248) startet den MBean-Server und registriert die drei MBeans. Als Adapter kommen sowohl der von »mx4j« mitgelieferte HTTP-Adapter als auch der HTML-Adapter der Referenz-Implementation zum Zuge, beide können ohne Probleme parallel betrieben werden, wenn sie auf unterschiedliche Ports konfiguriert sind. Als alternative Lösung ist bei Mx4j ein RMI-Adapter verfügbar, der sowohl klassisches Java-RMI als auch IIOP (das Corba-Protokoll Internet Inter-ORB Protocol) spricht. Damit lassen sich Swing-basierte Managementkonsolen implementieren.
Abbildung 3 zeigt die Sicht auf alle registrierten MBeans (manche davon sind Teil der Implementation). Abbildung 4 dagegen demonstriert die Änderung des Update-Intervalls. Beide Abbildungen verwenden den HTML-Adapter der Referenzimplementation, er ist übersichtlicher und einfacher zu bedienen als die Version aus Mx4j. Die Mx4j-Implementation wiederum hat den Vorteil, XML-basiert zu sein, und kann deshalb auch von anderen (XML-fähigen) Anwendungen weiterverarbeitet werden.
Unabhängigkeit bei Protokollfragen
Die Abfrage von Werten über den Browser oder eine Konsole ist bei Ad-hoc-Fragestellungen nützlich, für eine echte Überwachung aber ungeeignet. Dafür lässt sich in die MBean eine große Anzahl weiterer Funktionalitäten einbauen. Jede MBean kann zum Beispiel das »NotificationBroadcaster«-Interface implementieren und entsprechende Events absetzen. Objekte, die Interesse an diesen Events haben, müssen das »NotificationListener«-Interface implementieren und sich bei solchen MBeans registrieren. Hier findet also das normale Java-Event-Modell Verwendung, wie es von AWT bekannt ist.
Dadurch tauschen natürlich nur Objekte innerhalb der Anwendung Events aus. Die Spezifikation sagt zum aktuellen Zeitpunkt nichts darüber aus, in welcher Form diese Events an Managementanwendungen weitergegeben werden. Eine nützliche MBean bietet in diesem Zusammenhang die »mx4j«-Implementation mit einer SMTP-Bean. Bei geeigneter Konfiguration verschickt diese Bean Mails, wenn Events bei ihr eintreffen. Spezielle MBeans – die Monitor-MBeans – vereinfachen die Überwachung einzelner Attribute. Damit kann man es sich ersparen, die Eventauslösung aufwändig selbst zu kodieren.
Der Timer-Service bietet in diesem Zusammenhang die Möglichkeit, Events einmalig oder wiederholt im festen Abstand zu erzeugen. Statt wie im Beispiel einer intern verwalteten Ressource (Update-Intervall) hätte auch eine Timer- Service-MBean die Rolle des Taktgebers übernehmen können.
Designfragen
Damit ergibt sich ein wichtiger Gesichtspunkt für das Design von JMX-fähigen Anwendungen. Das Beispiel verwendet MBeans ohne eigene Logik, die MBeans-Methoden delegieren nur den Aufruf weiter an die eigentliche Serveranwendung. Dieses Design-Pattern ist nützlich, wenn die Anwendung existiert und nur eine MBean-Instrumentierung geschaffen werden soll.
Bei neuen Anwendungen bietet sich aber ein anderes Design an. Rückgrat der Anwendung ist der MBean-Server, alle Komponenten beziehungsweise Module der Anwendung sind als MBean realisiert. Im Beispiel gäbe es also ein CPU-Modul, ein Speicher- und ein I/O-Modul sowie weitere Module abhängig davon, welche Komponenten des Systems zu überwachen sind.
Neue Module könnte man ohne Aufwand selbst in eine laufende Anwendung integrieren. Einzige Forderung ist, dass die Klassen und Interfaces des neuen Moduls im Classpath der Anwendung liegen. Ein Management-Interface erzeugt und aktiviert dann die MBeans des Moduls dynamisch.
Genau diesen Ansatz verfolgt zum Beispiel der freie J2EE-Server JBoss. Hier sind alle Teile als MBean implementiert, so können Funktionalitäten wie der integrierte Webserver (Jetty oder Tomcat) einfach ausgetauscht werden. Management ist hier ein ohne Aufwand verfügbarer Zusatzeffekt, Modulverwaltung und Integration stehen im Mittelpunkt.
Zum echten Management gehört natürlich mehr, als JMX-fähige Anwendungen zu verwalten. Gerade in größeren Umgebungen sind anwendungsspezifische Konsolen und selbst Browser ungeeignet. Übergreifende Managementanwendungen verhindern, dass ganze Batterien von Bildschirmen in den Überwachungszentralen der Rechenzentren aufgestellt werden müssen, und ermöglichen außerdem die Zusammenführung unterschiedlicher Event-Typen.
Listing 2: »CpuMBean.java« |
29: public interface CpuMBean {
30: public int getCpuUserPercent();
31: }
|
Listing 3: »MemMBean.java« |
29: public interface MemMBean {
30: public long getMemUsed();
31: }
|
Listing 4: »UpdateIntervalMBean.java« |
29: public interface UpdateIntervalMBean {
30: public int getInterval();
31: public void setInterval(int interval);
32: }
|
finally{}
Wenn mal eine Anwendung ausfällt und der Switch, an dem der Server hängt, einen Fehler signalisiert, dann muss die eigentliche Ursache für den Ausfall gar nicht unbedingt die Anwendung sein, sondern vielmehr der Switch. In solchen Fällen wäre eine ausgefeilte Konsolidierungslogik erforderlich, um alle erdenklichen Fehlermeldungen zu bewerten und zu integrieren.
Für diese Aufgabe gibt es Systemmanagement-Software einer Reihe von Anbietern, die teilweise auch schon im Linux-Magazin vorgestellt wurde. Üblicherweise beruhen sie auf SNMP. Für die Integration von JMX-Anwendungen in SNMP-basierte Managementtools gibt es entsprechende SNMP-Adapter. Eine freie Implementation ist leider noch nicht verfügbar.
Trotzdem existieren rund um JMX schon sehr viele nützliche Open-Source-Entwicklungen; eine Suche auf Sourceforge fördert sie zu Tage. Auch Mx4j bietet mehr, als dieser Artikel beschreibt. Manches davon sprengt jedoch die Portabilität, aber darauf weist die Dokumentation stets hin. (uwo)
Listing 1: »LinuxInfo.java« |
022: import java.io.*;
023: import java.util.*;
024: import javax.management.*;
025: import mx4j.adaptor.http.*;
026: import com.sun.jdmk.comm.*;
027:
036: public class LinuxInfo implements Runnable {
037:
038: private long iMemTotal, iMemUsed, iMemFree;
039: private long iCpuUser1, iCpuNice1, iCpuSystem1, iCpuIdle1, iCpuTime1;
040: private long iCpuUser2, iCpuNice2, iCpuSystem2, iCpuIdle2, iCpuTime2;
041: private Object iCpuLock = new Object();
042: private int iUpdateInterval = 1000;
043:
050: private void readMem() throws IOException {
051: FileReader fr = new FileReader("/proc/meminfo");
052: ...
072: }
073:
080: public long getMemUsed() {
081: return iMemUsed;
082: }
083:
090: private void readCpu() {
091: try {
092: FileReader fr = new FileReader("/proc/stat");
093: ...
115: } catch (Exception e) {
116: }
117: }
118:
125: public int getCpuUserPercent() {
126: synchronized (iCpuLock) {
127: long diff = iCpuUser2 - iCpuUser1;
128: long total = iCpuUser2+iCpuSystem2+iCpuIdle2 -
129: (iCpuUser1+iCpuSystem1+iCpuIdle1);
130: return (int) Math.round((100.0*diff)/total);
131: }
132: }
133:
140: public void setInterval(int interval) {
141: iUpdateInterval = interval;
142: }
143:
150: public int getInterval() {
151: return iUpdateInterval;
152: }
153:
178: private void setupAgent() {
179: try {
180: MBeanServer server = MBeanServerFactory.createMBeanServer();
181: ObjectName cpuName = new ObjectName("LinuxInfo:name=cpu");
182: ObjectName memName = new ObjectName("LinuxInfo:name=mem");
183: ObjectName updateIntervalName =
184: new ObjectName("LinuxInfo:name=updateInterval");
185:
186: Cpu cpu = new Cpu(); cpu.setServer(this);
187: Mem mem = new Mem(); mem.setServer(this);
188: UpdateInterval updateInterval = new UpdateInterval();
189: updateInterval.setServer(this);
190:
191: server.registerMBean(cpu,cpuName);
192: server.registerMBean(mem,memName);
193: server.registerMBean(updateInterval,updateIntervalName);
194:
195: setupMX4J(server);
196: setupSunRI(server);
197:
198: } catch (Exception e) {
199: e.printStackTrace();
200: System.exit(3);
201: }
202: }
203:
210: private void setupMX4J(MBeanServer server) throws Exception {
211: HttpAdaptor adaptor = new HttpAdaptor();
212: ObjectName httpName = new ObjectName("mx4j:name=HttpAdaptor");
213: server.registerMBean(adaptor, httpName);
214: adaptor.setPort(8888);
215: adaptor.start();
216:
217: XSLTProcessor xslt = new XSLTProcessor();
218: ObjectName processorName = new ObjectName("mx4j:name=XSLTProcessor");
219: server.registerMBean(xslt,processorName);
220: server.setAttribute(httpName,new Attribute("ProcessorName",processorName));
221: }
222:
229: private void setupSunRI(MBeanServer server) throws Exception {
230: HtmlAdaptorServer adaptor = new HtmlAdaptorServer();
231: ObjectName htmlName = new ObjectName("sun:name=HtmlAdaptorServer");
232: server.registerMBean(adaptor,htmlName);
233: adaptor.setPort(9999);
234: adaptor.start();
235: }
236:
243: public static void main(String[] args) {
244: LinuxInfo li = new LinuxInfo();
245: Thread t = new Thread(li);
246: li.readCpu();
247: t.start();
248: li.setupAgent();
249: }
250: }
|
MBeans |
MBean ist die Abkürzung für Managed Bean. Diese Beans sind JMX-verwaltbare Java-Objekte. Es gibt insgesamt vier MBean-Typen, wobei die beiden letzten Typen nur Varianten des zweiten sind.
Standard MBeansStandard MBeans zeichnen sich dadurch aus, dass sie analog zu den normalen Beans einem vorgegebenen Design-Pattern mit Namenskonventionen folgen. Dadurch, dass die Klasse »Poolsize« zur Überwachung und Änderung eines Connection-Pools das Interface »PoolsizeMBean« implementiert, wird sie schon zu einer Standard MBean. Weiter gehende Anforderungen existieren nicht, sowohl das Interface als auch die Klasse können beliebige Methoden definieren und implementieren. Bean-üblich sind Getter/Setter-Methoden für Bean-Attribute verfügbar, zum Beispiel »getName()« und »setName()« für das Attribut »name«. Die Set-Methode muss nicht vorhanden sein, das Attribut ist dadurch automatisch read-only. Standard MBeans sind sehr mächtig (“Alles ist eine MBean”), aber unflexibel. Als Alternative gibt es die dynamischen MBeans. Dynamic MBeansBei Dynamic MBeans beschreiben Metadaten das Interface. Das unterläuft zwar die strenge Typisierung von Java, erlaubt andererseits aber die Änderung des Interface während des Programmablaufs. Im Gegensatz zu den Standard MBeans implementieren Dynamic MBeans ein vorgegebenes Interface (»javax.management.DynamicMBean«), zwischen der Klasse und dem Interface gibt es also keine Namensbeziehung mehr. Die Metadaten erlauben es außerdem, eine Bean sinnvoll zu beschreiben. Das ermöglicht es, sie einfacher über generische Managementanwendungen zu verwalten. Open MBeansOpen MBeans sind Dynamic MBeans, die verschiedenen Einschränkungen unterliegen. Open MBeans können zum Beispiel nicht beliebige Typen für ihre Attribute haben, es steht nur eine Reihe von Typen zur Auswahl. Hier spielt wieder die Idee einer generischen Managementumgebung eine Rolle: Ein Attribut vom Typ Integer kann jede JMX-Management-Umgebung setzen, den Typ “MeineSpezialKlasse” dagegen nicht (bestenfalls ist dies nach Änderungen am Code oder an der Laufzeitumgebung der Managementanwendung möglich). Model MBeansAuch Model MBeans sind Dynamic MBeans, sie werden aber nicht vom Anwendungsentwickler erstellt, sondern stehen bereits durch die JMX-Implementation zur Verfügung. Mit Model MBeans kann man vorhandene Ressourcen kapseln, ohne eigene MBeans erstellen zu müssen. Attribute und Methoden lassen sich über entsprechende APIs der Model MBean mitteilen. Es sei dahingestellt, ob dieser Weg tatsächlich einfacher und übersichtlicher ist, als eine eigene Wrapper-Standard-MBean zu erstellen. |
Infos |
| [1] Die JMX-Homepage bei Sun: [http://java.sun.com/products/JavaManagement/]
[2] Homepage von Mx4j: [http://mx4j.sourceforge.net] [3] JMX-Spezifikation: [http://jcp.org/aboutJava/communityprocess/final/jsr003/index3.html] [4] Listings zu diesem Artikel: [https://www.linux-magazin.de/Service/Listings/2003/09/Coffeeshop] |
Der Autor |
| Bernhard Bablok arbeitet bei der Allianz Versicherungs AG im Bereich Data-Warehouse-Systeme. Wenn er nicht Musik hört, mit dem Radl oder zu Fuß unterwegs ist, beschäftigt er sich mit Themen rund um Objektorientierung. Er ist unter [coffee-shop@bablokb.de] zu erreichen. |








