In diesem kurzen Beitrag soll es um Rendering Server im jadice web toolkit gehen. In diesem Zusammenhang möchte ich dabei auch kurz auf das Thema statelessness eingehen und erklären, wieso das aus meiner Sicht (aktuell?) keine gute Idee ist.

Jeder, der das jadice web toolkit bereits in größerem Umfang betreibt, weiß, dass man beim horizontalen Skalieren (mehrere Server) Sticky Sessions benötigt.

Technisch ist es zwischenzeitlich so, dass das jadice web toolkit aber durchaus auch ohne HTTP-Session zurechtkommt. Was würde nun also passieren, wenn es drei jadice web toolkit Instanzen gibt, mit einem vorgeschalteten Load-Balancer, der die Anfragen einfach nach dem Round-Robin Verfahren abwechselnd auf die drei Server verteilt? Eigentlich nichts, der Benutzer würde davon nichts merken.

Die erste Anfrage würde bei Server A landen, dieser würde das anzuzeigende Dokument aus dem Archiv laden. Server A würde dann die Struktur des Dokuments (Anzahl der Seiten, …) an den Client streamen, woraufhin der Client pro Seite eine Anfrage an den Server schickt, um diese Seite rendern zu lassen (unsere so genannten Tile-Requests). Der erste Tile-Request würde dann bei Server B landen. Für Server B ist das Dokument unbekannt (nicht im lokalen Cache), es würde daher der Recovery-Mechanismus des jadice web toolkit zum Tragen kommen, der Server würde sich das komplette Dokument ebenfalls aus dem Archiv holen und Seite 1 anzeigen. Für Seite 2 des Dokuments würde der Tile-Request dann auf Server C landen und das selbe würde nochmals passieren. Am Ende hätte also jeder Server das Dokument aus dem Archiv geladen, die Struktur gelesen und interpretiert, um dann eine oder mehrere Seiten zu rendern. Jeder lokale Cache eines Servers hätte nun dutzende Java-Objekte, die unnötig Speicher verbrauchen. Es macht daher Sinn, das Load-Balancing so zu konfigurieren, dass ein Benutzer stets auf den selben Server geleitet wird. 

Proof of Concept

Kommen wir nun aber zu den Rendering Servern. Hintergrund dieses PoCs sind unter anderem Überlegungen aus meinem ersten Kundenprojekt. Stateless ist hip und modern und der Betrieb muss weniger konfigurieren. Wie könnte man also ein stateless jadice web toolkit bauen, jedoch ohne die Einschränkung des zeitlichen Overhead (wegen mehrfachem Archivzugriff) und ohne den unnötigen RAM-Verbrauch?

Die Idee war hierbei die folgende:

Das Kundenportal integriert das jadice web toolkit wie gewohnt, zusammen mit der Fachanwendung und deren Logik. Hiervon gibt es mehrere Instanzen. Im Hintergrund gibt es spezielle Rendering Server, welche lediglich das jadice web toolkit bereitstellen. Zwischen Kundenportal und Rendering Server gibt es jedoch ebenfalls einen Load Balancer.

In dem PoC wurde das jadice web toolkit so angepasst, dass die erste Seite auf dem Kundenportal gerendert wird (wir legen bei den jadice Produkten immer viel Wert auf ein schnelles First-Page-Display), alle weiteren Seiten von den Rendering-Servern. Daraus ergeben sich folgende Vor- und Nachteile:

  • Schnelles First-Page-Display
  • Dem Kundenportal stehen mehr Ressourcen zur Verfügung, als wenn es das jadice web toolkit inkl. Rendering und die Fachanwendung bereitstellen muss
  • Die Rendering Server benötigen, bis auf den DocumentDataProvider, keine Fachlogik und kein Frontend
  • Ein Archivzugriff erfolgt doppelt, das Dokument muss 1x im Kundenportal (für die erste Seite) eingelesen werden und 1x auf dem Rendering Server
  • Die Rendering Server können z.B. per Kubernetes dynamisch skaliert (zu- und abgeschaltet) werden

Dieser PoC könnte relativ einfach ins Produkt überführt werden. Wir verfolgen hier mehrere Ideen weiter, sind aber hier auch offen für Feedback und Ideen unserer Kunden.

Richtiges Statelessness

Im Szenario aus dem PoC kommen wir noch immer nicht ohne Load Balancer und Session Affinity aus. Um das zu erreichen, bedarf es einer grundsätzlich anderen Architektur unseres Dokumenten-Modells und dem Rendering. Wieso das so ist, wird im nächsten Punkt etwas klarer.

Verteilter Cache

Der aufmerksame Leser hat vielleicht bemerkt, dass es um Objekte in einem lokalen Cache geht, die benötigt werden, um eine Seite zu rendern. Ich habe daher einen anderen PoC versucht, den lokalen Cache durch einen verteilten Cache (Hazelcast) zu ersetzen. Die erste Hürde dabei ist, das jadice Document zu serialisieren. Abgesehen von der Rechenzeit und dem Netzwerk-Overhead ist dies aber mit OpenPortfolio recht einfach möglich. Allerdings gibt es auch hier wieder Nachteile: der Lesevorgang eines Dokuments in jadice funktioniert streaming-basiert. Sobald die Struktur des Dokuments (Anzahl der Seiten, …) eingelesen wurde, wird der Client benachrichtigt und dieser beginnt das Rendering der ersten Seite anzufordern. jadice kann dann bereits Seiten rendern, während der Lesevorgang des Dokuments noch im Gange ist. Serialisiert werden kann jedoch (aktuell zumindest) erst, wenn das Dokument fertig eingelesen wurde. Man erkauft sich mit einem verteilten Cache also nicht nur den Serialisierungs- und Netzwerk-Overhead, sondern man muss auch warten, bis das Dokument vollständig geladen wurde, bevor man etwas anzeigen kann.

Blick nach vorne

Mit unserem bisherigen Dokumenten-Modell ist nach aktuellen Erkenntnissen ein stateless Backend für das jadice web toolkit leider nicht umsetzbar. Unsere Gedanken kreisen allerdings weiter um dieses Thema.

Meiner persönlichen Meinung nach ist jedoch auch im Jahr 2024 eine Architektur, welche Session Affinity erfordert, nach wie vor eine gute Lösung, da diese wohl immer die schnellsten Antwortzeiten liefern kann. Hier entfallen nämlich Serialisierung und Netzwerk-Overhead, alles läuft in einer JVM.

Haben Sie Gedanken oder Erfahrungen zu diesem Thema? Kontaktieren Sie mich gerne direkt, ich freue mich über einen regen Austausch!

Einige weiterführende Links: