Clustering, also ein und dieselbe Logik von mehreren JVM Instanzen parallel abarbeiten zu lassen, ist in der JavaEE Welt die wichtigste Strategie für besseres Skalenverhalten. Das gesamte JavaEE Gebäude ist um diesen zentralen Punkt herum gebaut! Im Idealfall funktioniert eine JavaEE Implementierung vollkommen unabhängig davon, ob sie im Cluster oder in einer Single-Instanz betrieben wird. Leider weisen die Experten zurecht darauf hin, dass das ein Mythos ist und nennen gute Gründe.
Dabei ist es doch einfach: sämtliche JavaEE Komponenten müssen konform zur Spezifikation implementiert werden. Das hier ist der Versuch, die wichtigsten davon einmal festzuhalten. Alle genannten Kriterien sind obligatorisch für JavaEE Konformität. Eine Verletzung der nachfolgenden Anforderungen bedingt aus der Sicht des JavaEE Containers fehlerhaften Code, der aber möglicherweise erst in geclusterten Umgebungen auffallen wird.
Sessions im Web und EJB Container
Ein JavaEE Servercluster muss bei Bedarf die Sessions des Webcontainers oder EJB Containers über JVM Grenzen hinweg transportieren. Dazu passiviert er die Session auf der einen JVM, überträgt die serialisierten Daten und aktiviert sie auf der anderen JVM. Dieser Mechanismus muss entsprechend der JavaEE Spec unterstützt werden und für den Umgang mit Sessiondaten lässt sich zusammenfassen:
Passivierung/Aktivierung muss nicht zwingend auf Standard-Serialisierung beruhen, aber die Spec garantiert, dass serialisierbare Klassen ohne Fehler passiviert/aktiviert werden. Sessionattribute (und alle referenzierten Klassen) müssen damit potenziell Serializable implementieren können und sollten dies auch tun. Eine Implementierung von readObject und writeObject der Sessionattribute für die Behandlung von nicht serialisierbaren Ressourcen ist nicht statthaft, da der Server diese Methoden nicht notwendig rufen wird. Korrekte Lösung: im Web Container implementieren die Sessionattribute HttpSessionActivationListener, im EJB Container sind für die betreffenden Stateful Session Beans ejbPassivate und ejbActivate zu implementieren. Statt das Schlüsselworts transient zu benutzen sind die betreffende Member bei der Passivierung auf null zu setzen und bei der Aktivierung wieder zu befüllen.
Der ServletContext ist nicht serialisierbar, damit ist ein setAttribute für die Implementierung von Anwendungslogik nutzlos, es wird im Cluster nicht funktionieren.
EJBLocale Objekte müssen über ihr Handle in der Passivierung/Aktivierung behandelt werden:
Passivierung: myHandle = myEJB.getHandle(); // ist serialisierbar myEJB = null; // Aktivierung: Object obj = myHandle.getEJBObject(); myEJB = (MyEJB) PortableRemoteObject.narrow(obj, MyEJB.class); myHandle = null;Allgemein sind Sessiondaten so klein wie möglich zu halten und sollten in serialisierter Form eine bestimmte Größe nicht überschreiten. Beim Modifizieren von Attributen in einer HTTPSession ist setAttribute zu rufen (nur so wird in manchen Containern Sessionreplication angestoßen). SessionContext, der JNDI Context auf java:comp/env, ResourceManagerConnectionFactory Instanzen, UserTransaction Instantzen, EJB Timer Instanzen, EJBRemote Instanzen, EJBRemoteHome Instanzen, EntityLocale und EntityLocaleHome Instanzen werden korrekt vom Container serialisiert und deserialisiert.
Über JVM Grenzen hinweg gemeinsam genutzte Ressourcen sind als Resorce Referenzen im Deploymentdescriptor zu hinterlegen. Der Application Assembler kann dann diesen Referenzen skalierende Infrastruktur zuordnen (zB Resource Manager Connection Factory, JMS Connection Factory) und im JNDI des Clusters hinterlegen. Der einer JavaEE Komponente zugeordnete JNDI-Rootcontext beginnt damit immer mit dem logischen Pfad: java:comp/env. Nur clusterfähige Ressourcen verwenden, insbesondere ist das Filesystem zur Speicherung eines Zustands ungeeignet. Im EJB Container ist ohnenhin klargestellt, dass Zugriffe auf das lokale Filesystem nicht erlaubt sind. Im Servletcontainer ist so ein Zugriff laut Spec erlaubt - hier ist entsprechend Vorsicht geboten.
Jedes Servlet ist per JVM ein Quasi-Singleton. Im Cluster exisitert dann pro JVM eine Servletinstanz. Die Geschäftslogik eines Servlets darf somit nicht vom Singleton-Charakter eines Servlets ausgehen.
Die Verarbeitungslogik einer Stateful Session Beans darf nicht davon ausgehen, dass einem Client bei jedem Aufruf ein und dieselbe Instanz einer Stateful Session EJB darbietet. Hinweis: das gilt nicht nur im Cluster, wird da aber schneller auffallen.
Statische Variablen sind im Kontext einer Bean oder eines Servlets problematisch. Ausnahme: es handelt sich um Felder mit read-only Semantik und statischer Initialisierung.
Bei der Verwendung von Caches in geclusterten Umgebungen ist sicherzustellen, dass die verwendeten Cache-Implementierungen Clustering unterstützen. Zusätzlich ist zu nachzuweisen, dass der Laufzeitaufwand bei der Betreibung des Caches geringer ausfällt als eine Lösung ohne Caching.
Der EJB Timer Service ist ab EJB2.1 vollständig in die JavaEE Laufzeit integriert und damit per se clusterfähig. Der Einsatz von Quartz in geclusterten Umgebungen unterliegt Restriktionen, die unbedingt berücksichtigt werden müssen:
Bei der Implementierung der Singleton Pattern in JavaEE Umgebungen müssen Besonderheiten berücksichtigt werden. Zunächst einmal ist festzustellen, dass das Singleton Pattern die EJB Programmierrestriktionen verletzt. Die Singleton-Instanzen der Knoten des Clusters besitzen keine inhärente Möglichkeit, ihren Zustand untereinander zu synchronisieren. Alle Bemühungen, eine solche Synchronisierung nachträglich zu implementieren, nutzen synchronisierten Code, werden das Skalierverhalten der Anwendung negativ beeinflussen und können den Server in einen unvorhersagbaren Zustand führen. Die Ausnahme: Singletons mit einer read-only Semantik besitzen nach ihrer Initialisierung einen unveränderlichen Zustand, eine Synchronisierung ist nicht erforderlich. Ein typisches Einsatzfeld für solche Singletons ist das Cachen statischer Ressourcen. Bei der Implementierung sind folgende Regeln einzuhalten:
Servlet2.4 Spec: SRV.7.7 Important Session Semantics
EJB2.1 Spec: 7.4 Conversational State of a Stateful Session Bean
J2EE clustering, Part 1
J2EE clustering, Part 2
Uncover the hood of J2EE Clusteriung