Komplexe konfigurierbare Webanwendungen mit der JBoss Suite

Lessons Learned

Christian Groth
Performance und Last

Als letzter größerer Themenbereich sollen noch einige Probleme mit den allseits geliebten nicht funktionalen Anforderungen erläutert werden. Konzentrieren wollen wir uns hier auf die Bemühungen, die Anwendung möglichst performant und skalierbar zu gestalten, sodass diese auch unter Last akzeptable Antwortzeiten liefert.

Bijection

Nachdem die ersten Erfahrungen mit den Technologien gesammelt wurden und erste Features und Workflows implementiert worden sind, wurde die Performance der Anwendung mit JMeter-Tests überprüft. Diese zeigten schnell, dass die Anwendung nicht zufriedenstellend skalierte und unter Last die Auslieferungszeiten der Seiten extrem stieg. Mittels Profiler war schnell die Bijection als Problem ausgemacht. Hierzu muss man sich etwas genauer anschauen wie die Bijection arbeitet. Bei jedem Zugriff auf eine Seam-Komponente werden die In- und Outjections durchgeführt. Für uns bedeutete das konkret, dass jeder Zugriff aus der XHTML-Seite heraus auf eine Seam-Komponente teuer war. Dabei waren 95 % der Zugriffe nur Abfragen vorher berechneter Daten. Für dieses Problem boten sich zwei mögliche Lösungen an:

  • Alle Daten in den Kontext outjecten und diese direkt von dort lesen
  • Alle Daten in einer State-Klasse kapseln, die entweder keine Seam-Komponente ist und selber im Kontext liegt oder mit @BypassInterceptors annotiert ist, was die In- und Outjection unterbindet

Den Weg in das Projekt hat die zweite Lösung gefunden, da pro Workflow recht viele Daten anfallen und diese entsprechend in einer State-Klasse gruppiert werden sollten. Das hat den Vorteil, dass nicht alles „flach“ im Kontext liegt und man als Entwickler leichter den Überblick behält.

JSF-Performance

Jedoch war die Bijection nicht das einzige Performanceproblem. Weitere Analysen mit dem Profiler haben schließlich ergeben, dass JSF und Richfaces einiges an Zeit verschlingen. Die Werte lagen bei komplexen Seiten bei ca. 15 bis 20 %, bei einfachen Seiten ohne viel Businesslogik kam der Anteil auch schon mal auf 50 %. Eine Internetrecherche bestätigte dieses Problem, das in einigen Foren diskutiert wurde. Als Alternative wird oft Apache Wicket vorgeschlagen. Diesen durchaus sehr aufwändigen Schritt des Technologiewechsels haben wir zu diesem Zeitpunkt im Projekt allerdings nicht mehr evaluiert, weswegen wir hier leider keine Erfahrungen im Zusammenspiel von Wicket und Seam anbieten können.

Verfügbarkeit

Neben der Performance spielt natürlich die Verfügbarkeit einer Anwendung eine wichtige Rolle. Diese sollte mit den Clustering Features des JBoss Application Servers erhöht werden. Ziel war es, eine Konfiguration von zunächst zwei Servern zu erstellen, die sich gegenseitig via Sessionreplikation synchronisieren und so ein Failover realisieren können. Die Probleme lagen jedoch im Detail der Sessionreplikation verborgen. Zunächst muss sichergestellt sein, dass alle Objekte, die in einem der Seam-Kontexte und damit auch in der Websession landen, serialisierbar sind. Problematischer wurde es schon, als wir feststellen mussten, dass die kompilierten RuleBases von JBoss Rules nicht serialisierbar waren. So musste jeder Server seinen eigenen Regel-Cache aufbauen. Den Show-Stopper lieferte dann leider Hibernate. Nach dem (simulierten) Ausfall eines Servers wurde der nächste Request der Session zwar auf dem anderen Server übernommen, jedoch gab es Probleme mit dem Lazy Loading. Die replizierten Entitäten wurden auf dem neuen Knoten anscheinend nicht korrekt mit der neuen Hibernate-Session verbunden. Zeit für eine detaillierte Ursachenforschung blieb leider nicht, da wir an diesem Punkt aufgrund des nahenden Produktivgangs des Systems die Versuche abbrechen mussten. So haben wir uns für vorgeschaltete Apache-Webserver, Sticky-Sessions und eine Anbindung über AJP entschieden. Die Anwendung bleibt bei einem Serverausfall also grundsätzlich erreichbar, auch wenn eine einzelne Websession eines Anwenders nicht gerettet werden kann.

Rückblickend bleibt nach mehr als einem Jahr Produktivbetrieb jedoch festzuhalten, dass bisher nicht ein einziges Problem der Verfügbarkeit mit einem der beiden Server aufgetreten ist, was nur für die Grundstabilität der eingesetzten Software spricht.

Rück- und Ausblick

Zusammenfassend bleibt zu sagen, dass das Projekt mit vielen neuen Technologien auf der grünen Wiese gestartet wurde und die Entwickler nach den üblichen Einarbeitungshürden gut mit dem Zusammenspiel der verschiedenen Technologien zurechtkamen. Natürlich sind in diesem Artikel eher die Probleme und damit die negativen Aspekte aufgeführt, das soll jedoch nicht bedeuten, dass diese Technologien nicht für den produktiven Einsatz geeignet sind. Sicherlich muss immer im Einzelfall geprüft werden, ob die hier verwendeten Technologien in genau der Zusammenstellung eingesetzt werden sollen. Der Vorteil liegt auf der Hand: An Integrationsarbeit mussten wir nichts mehr leisten. Aus Sicht der Performance wäre es sicherlich spannend gewesen, mit Apache Wicket noch eine Alternative zu JSF zu evaluieren.

Dennoch soll zum Schluss ein relevanter Punkt nicht unterschlagen werden: Die Technologien haben untereinander einige Abhängigkeiten. So sollte man (zumindest im konkreten Fall) für den Umstieg auf eine neuere Seam-Version auch die JBoss-Version und entsprechende weitere Abhängigkeiten aktualisieren. In unserem Fall bedeutet das unter anderem den Umstieg auf den JBoss Application Server in der Version 5. Dieser Umstieg ist natürlich mit einigem Aufwand verbunden und kann nicht mal eben durchgeführt werden. Abgeschlossen werden soll der Artikel mit einer Liste der unserer Meinung nach wichtigsten „Lessons Learned“:

  • Komplexes Conversation Handling ist nur bedingt möglich; kaum Informationen zu den wichtigen Schnittstellen, daher sind eigene Erweiterungen nur schwer zu stabilisieren
  • Entitäten in der View-Schicht zu verwenden ist sehr angenehm, jedoch lauern Probleme bei der Weitergabe von Entitäten in andere Conversations in Kombination mit dem Extended Persistence Context
  • Konzepte zur klaren Initialisierung von Pageflows fehlen
  • Das Event Handling von Seam ist stets synchron und sollte nicht Conversation-übergreifend benutzt werden
  • Der Standard-CassLoader bremst JBoss Rules aus, ansonsten skaliert JBoss Rules bezüglich Regelanzahl und Größe des Kontextes wie man es sich wünscht und es konnten keine Performanceprobleme gefunden werden
  • Die Kombination von MVEL und Hibernate Proxies führte zu schwer reproduzierbaren Exceptions, man sollte darauf achten, keine Hibernate Proxies in den Regelkontext zu legen
  • Seam-Bijection kann schnell zu einem Performancekiller werden, eine strikte Trennung in Controller und State schafft hier Abhilfe
  • JSF und Richfaces haben (zumindest in unserem Projekt) einen großen Anteil an der Gesamtperformance des Systems und zu Tests bezüglich der Skalierbarkeit des Systems können wir nur raten
  • Das Clustering aufzusetzen scheiterte an einigen Details und ist bei Weitem nicht so einfach wie zunächst vermutet

Christian Groth (groth[at]objectcode.de) arbeitet als Senior-Developer und Architekt für Java-EE-Anwendungen bei der ObjectCode GmbH [5] in Lünen und widmet sich berufsbegleitend seinem Master-Studium an der Fernuniversität in Hagen.

Geschrieben von
Christian Groth
Kommentare

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht.