„First make it work, then make it fast“ ist nicht immer richtig

Vielleicht haben Sie schon von diesem Ratschlag für Programmierende gehört: „First make it work, then make it fast“ (Erst muss es funktionieren, dann schnell werden).. Dies ist nicht ganz falsch. Ohne den richtigen Kontext wird es jedoch oft falsch interpretiert und nicht korrekt angewendet.

Der Rat sollte Entwicklungspersonen davon abhalten, Code vorzeitig zu optimieren, der möglicherweise nie ausgeführt wird – oder so selten ausgeführt wird, dass eine Optimierung keine ausreichenden Auswirkungen hätte, um den Aufwand für die Optimierung zu rechtfertigen. Darüber hinaus könnte eine Optimierung zu komplexerem Code führen und damit Fehler verursachen. Wenn Sie Entwicklerin oder Entwickler sind, verbringen Sie also nicht zu viel Zeit damit, jede einzelne Codezeile zu mikrooptimieren. Stellen Sie einfach sicher, dass Sie die richtigen Datenstrukturen, Algorithmen und Bibliotheken auswählen und warten Sie, bis die Hotspot-Analyse eines Profilers zeigt, wo eine gründlichere Optimierung die Gesamtleistung steigern könnte.

Architektonische Entscheidungen und Artefakte

Der Rat „Erst muss es funktionieren, dann schnell werden“ ist jedoch völlig falsch, wenn es um „architektonische“ Entscheidungen geht. Was sind architektonische Entscheidungen? Einfach gesagt, sind das die Entscheidungen, die später nur teuer, schwierig und/oder unmöglich zu ändern sind. Denken Sie daran, dass „teuer“ manchmal dasselbe ist wie „unmöglich“. Wenn beispielsweise Ihr Projekt über kein Budget mehr verfügt, können teure Änderungen nicht mehr implementiert werden. Infrastrukturveränderungen sind die allerersten Veränderungen in dieser Kategorie, die den meisten in den Sinn kommen. Aber es gibt auch eine andere Art von „architektonischen“ Artefakten, die sich sehr schwierig ändern lassen:

  1. Code-Abschnitte im „Zentrum“ einer Anwendung, auf die viele andere Teile angewiesen sind. Wenn Sie diese ändern, müssen alle Abhängigkeiten auf einmal geändert und neu getestet werden.

  2. Artefakte, die in einem asynchronen, zeitabhängigen Szenario involviert sind, in dem die Eingabe – und damit das Verhalten des Systems – sehr zufällig variieren kann. Änderungen können hier unvorhersehbare Auswirkungen haben und schwer zu testen sein.

  3. Software-Muster, die in allen Teilen des Systems immer wieder verwendet und wiederverwendet werden. Wenn sich das Software-Muster als suboptimal erweist, müssen alle Artefakte, die das Muster verwenden, neu codiert werden.

Erinnern Sie sich? Am Anfang dieser Seite haben wir gesagt, dass der Dispatcher ein wesentlicher Bestandteil einer AEM-Anwendung ist. Der Zugriff auf eine Web-Anwendung ist sehr zufällig – Benutzende kommen und gehen zu unvorhersehbaren Zeiten. Letztendlich werden alle Inhalte im Dispatcher zwischengespeichert (oder sollten es zumindest). Wenn Sie also genau aufgepasst haben, haben Sie vielleicht gemerkt, dass das Caching als „architektonisches“ Artefakt angesehen werden kann und daher von allen Mitgliedern des Teams, Entwicklungspersonen und Admins gleichermaßen verstanden werden sollte.

Wir sagen nicht, dass Entwicklungspersonen den Dispatcher tatsächlich konfigurieren sollten. Sie müssen jedoch die Konzepte – insbesondere die Grenzen – kennen, um sicherzustellen, dass ihr Code auch vom Dispatcher genutzt werden kann.

Der Dispatcher verbessert die Geschwindigkeit des Codes nicht auf magische Weise. Entwicklungspersonen müssen ihre Komponenten daher unter Berücksichtigung des Dispatchers erstellen. Dafür müssen sie wissen, wie er funktioniert.

Dispatcher-Caching – Grundprinzipien

Dispatcher als Caching-HTTP – Lastenausgleich

Was ist der Dispatcher und warum wird er überhaupt als „Dispatcher“ bezeichnet?

Der Dispatcher ist:

  • zuallererst ein Cache

  • ein Reverse-Proxy

  • ein Modul für den Apache-httpd-Webserver, das die Vielseitigkeit von Apache um AEM-bezogene Funktionen erweitert und reibungslos mit allen anderen Apache-Modulen zusammenarbeitet (wie SSL oder sogar SSI-Einbindungen, wie wir später sehen werden)

In den Anfängen des Internets rechnete man mit ein paar hundert Besuchenden auf einer Website. Ein Setup mit einem Dispatcher, der die Last der Anfragen an eine Reihe von AEM-Veröffentlichungs-Servern „dispatchen“ (ausgewogen verteilen) konnte, war in der Regel ausreichend – daher der Name „Dispatcher“. Heutzutage wird dieses Setup jedoch nicht mehr sehr häufig verwendet.

Später in diesem Artikel werden wir verschiedene Möglichkeiten zum Einrichten von Dispatchern und Veröffentlichungssystemen sehen. Beginnen wir zunächst mit einigen Grundlagen der HTTP-Zwischenspeicherung.

Grundlegende Funktionalität eines Dispatcher-Caches

Grundlegende Funktionalität eines Dispatcher-Caches

Die fundamentalen Grundlagen des Dispatchers werden hier erläutert. Der Dispatcher ist ein einfacher Reverse-Proxy zum Zwischenspeichern mit der Möglichkeit, HTTP-Anfragen zu empfangen und zu erstellen. Ein normaler Anfrage-/Antwortzyklus sieht wie folgt aus:

  1. Benutzende fordern eine Seite an
  2. Der Dispatcher prüft, ob bereits eine gerenderte Version dieser Seite vorhanden ist. Angenommen, es handelt sich um die allererste Anfrage für diese Seite und der Dispatcher kann keine lokale zwischengespeicherte Kopie finden.
  3. Der Dispatcher fordert die Seite vom Veröffentlichungssystem an
  4. Im Veröffentlichungssystem wird die Seite durch eine JSP- oder eine HTL-Vorlage gerendert
  5. Die Seite wird an den Dispatcher zurückgegeben
  6. Der Dispatcher speichert die Seite zwischen
  7. Der Dispatcher gibt die Seite an den Browser zurück
  8. Wenn dieselbe Seite ein zweites Mal angefordert wird, kann sie direkt aus dem Dispatcher-Cache bereitgestellt werden, ohne dass sie erneut in der Veröffentlichungsinstanz gerendert werden muss. Dadurch spart man Wartezeiten für Benutzende und CPU-Zyklen auf der Veröffentlichungsinstanz.

Wir haben im letzten Abschnitt über „Seiten“ gesprochen. Dasselbe Schema gilt jedoch auch für andere Ressourcen wie Bilder, CSS-Dateien, PDF-Downloads usw.

Zwischenspeicherung von Daten

Das Dispatcher-Modul nutzt die Funktionen, die der hostende Apache-Server bietet. Ressourcen wie HTML-Seiten, Downloads und Bilder werden als einfache Dateien im Apache-Dateisystem gespeichert. So einfach ist es.

Der Dateiname wird von der URL der angeforderten Ressource abgeleitet. Wenn Sie die Datei /foo/bar.html anfordern, wird sie beispielsweise unter /var/cache/docroot/foo/bar.html gespeichert.

Grundsätzlich können Sie, wenn alle Dateien zwischengespeichert und daher statisch im Dispatcher gelagert werden, das Kabel des Veröffentlichungssystems ziehen und der Dispatcher würde als einfacher Webserver fungieren. Aber dies dient nur der Veranschaulichung des Grundsatzes. Das wahre Leben ist komplizierter. Man kann nicht alles zwischenspeichern, und der Cache ist nie vollständig „voll“, da die Anzahl der Ressourcen aufgrund der dynamischen Natur des Rendervorgangs unbegrenzt sein kann. Das Modell eines statischen Dateisystems hilft, ein grobes Bild der Funktionen des Dispatchers zu erzeugen. Und es hilft, die Einschränkungen des Dispatchers zu erklären.

AEM-URL-Struktur und Dateisystemzuordnung

Um den Dispatcher detaillierter zu verstehen, schauen wir uns die Struktur einer einfachen Beispiel-URL erneut an. Sehen wir uns das folgende Beispiel an:

http://domain.com/path/to/resource/pagename.selectors.html/path/suffix.ext?parameter=value&otherparameter=value#fragment

  • http bezeichnet das Protokoll

  • domain.com ist der Domain-Name

  • path/to/resource ist der Pfad, unter dem die Ressource in CRX und anschließend im Dateisystem des Apache-Servers gespeichert wird

Hier unterscheiden sich die Dinge ein wenig zwischen dem AEM-Dateisystem und dem Apache-Dateisystem.

In AEM sieht es so aus:

  • pagename ist der Titel der Ressourcen

  • selectors steht für eine Reihe von Selektoren, die in Sling verwendet werden, um zu bestimmen, wie die Ressource gerendert wird. Eine URL kann eine beliebige Anzahl von Selektoren aufweisen. Sie werden durch einen Punkt getrennt. Ein Selektorabschnitt könnte z. B. „deutsch.handy.schick“ sein. Selektoren dürfen nur Buchstaben, Ziffern und Gedankenstriche enthalten.

  • html als letzter der „Selektoren“ wird als Erweiterung bezeichnet. In AEM/Sling bestimmt er auch teilweise das Rendering-Skript.

  • path/suffix.ext ist ein pfadähnlicher Ausdruck, der ein Suffix der URL sein kann. Er kann in AEM-Skripten verwendet werden, um die Darstellung einer Ressource weiter zu steuern. Es gibt später einen ganzen Abschnitt über diesen Teil. Zunächst sollte es ausreichen, zu wissen, dass man ihn als zusätzlichen Parameter verwenden kann. Suffixe benötigen eine Erweiterung.

  • ?parameter=value&otherparameter=value ist der Abfrageabschnitt der URL. Er wird verwendet, um beliebige Parameter an AEM zu übergeben. URLs mit Parametern können nicht zwischengespeichert werden. Daher sollten Parameter auf Fälle beschränkt werden, in denen sie absolut erforderlich sind.

  • #fragment, der Fragmentteil einer URL, wird nicht an AEM übergeben, sondern nur im Browser benutzt; entweder in JavaScript-Frameworks als „Routing-Parameter“ oder zum Springen zu einem bestimmten Teil der Seite.

In Apache (bezieht sich auf das folgende Diagramm)

  • wird pagename.selectors.html als Dateiname im Dateisystem des Caches verwendet.

Wenn die URL ein Suffix path/suffix.ext hat, dann

  • wird pagename.selectors.html als Ordner erstellt,

  • path ist ein Ordner im Ordner pagename.selectors.html,

  • suffix.ext ist eine Datei im Ordner path. Hinweis: Wenn das Suffix keine Erweiterung aufweist, wird die Datei nicht zwischengespeichert.

Dateisystem-Layout nach Abrufen von URLs vom Dispatcher

Dateisystem-Layout nach Abrufen von URLs vom Dispatcher

Grundlegende Einschränkungen

Die Zuordnung zwischen einer URL, der Ressource und dem Dateinamen ist ziemlich einfach.

Sie haben jedoch möglicherweise einige Fallen bemerkt:

  1. URLs können sehr lang werden. Das Hinzufügen des „Pfad“-Teils einer /docroot auf dem lokalen Dateisystem kann die Grenzen einiger Dateisysteme leicht überschreiten. Das Ausführen des Dispatchers in NTFS unter Windows kann eine Herausforderung sein. Bei Linux sind Sie jedoch sicher.

  2. URLs können Sonderzeichen und Umlaute enthalten. Dies ist normalerweise kein Problem für den Dispatcher. Beachten Sie jedoch, dass die URL an vielen Stellen Ihrer Anwendung interpretiert wird. Schon oft haben wir merkwürdiges Verhalten einer Anwendung beobachtet – nur um herauszufinden, dass ein Teil des selten verwendeten (benutzerdefinierten) Codes nicht gründlich auf Sonderzeichen getestet wurde. Sie sollten diese also vermeiden, wenn möglich. Wenn nicht, planen Sie gründliche Tests ein.

  3. In CRX verfügen Ressourcen über Unterressourcen. Eine Seite enthält beispielsweise eine Reihe von Unterseiten. Dies kann nicht in einem Dateisystem abgeglichen werden, da Dateisysteme entweder Dateien oder Ordner haben.

URLs ohne Erweiterung werden nicht zwischengespeichert

URLs müssen immer über eine Erweiterung verfügen. Sie können zwar URLs ohne Erweiterungen in AEM bereitstellen. Diese URLs werden aber nicht im Dispatcher zwischengespeichert.

Beispiele

http://domain.com/home.html ist zwischenspeicherbar

http://domain.com/home ist nicht zwischenspeicherbar

Dieselbe Regel gilt, wenn die URL ein Suffix enthält. Das Suffix muss über eine Erweiterung verfügen, um zwischenspeicherbar zu sein.

Beispiele

http://domain.com/home.html/path/suffix.html ist zwischenspeicherbar

http://domain.com/home.html/path/suffix ist nicht zwischenspeicherbar

Sie fragen sich vielleicht, was passiert, wenn der Ressourcenteil keine Erweiterung hat, aber das Suffix schon? Nun, in diesem Fall hat die URL überhaupt kein Suffix. Sehen Sie sich das folgende Beispiel an:

Beispiel

http://domain.com/home/path/suffix.ext

Das /home/path/suffix ist der Pfad zur Ressource … also ist kein Suffix in der URL.

Zusammenfassung

Fügen Sie sowohl dem Pfad als auch dem Suffix immer Erweiterungen hinzu. SEO-bewusste Menschen argumentieren manchmal, dass Sie dadurch in den Suchergebnissen weiter unten landen. Eine nicht zwischengespeicherte Seite wäre jedoch extrem langsam und würde sogar noch weiter nach unten geraten.

Konflikte bei Suffix-URLs

Angenommen, Sie haben zwei gültige URLs

http://domain.com/home.html

und

http://domain.com/home.html/suffix.html

Beide sind in AEM absolut gültig. Auf Ihrem lokalen Entwicklungs-Computer würden Sie kein Problem sehen (ohne Dispatcher). Wahrscheinlich stoßen Sie auch bei UAT- oder Belastungstests nicht auf Probleme. Das Problem, vor dem wir stehen, ist derart subtil, dass es bei den meisten Tests nicht erkennt wird. Es wird Sie hart treffen, wenn die Belastung nicht höher sein könnte und Sie nur begrenzt Zeit haben, um dieses Problem zu beheben. Wobei Ihnen für die Lösung wahrscheinlich der Server-Zugriff und die Ressourcen fehlen. Wir haben das alles schon erlebt …

Aber was ist nun das Problem?

home.html in einem Dateisystem kann entweder eine Datei oder ein Ordner sein, aber nicht beides gleichzeitig wie in AEM.

Wenn Sie home.html erstmalig anfordern, wird eine Datei erstellt.

Bei nachfolgenden Anfragen an home.html/suffix.html werden zwar gültige Ergebnisse zurückgegeben, allerdings „blockiert“ die Datei home.html die Position im Dateisystem. home.html kann nicht ein zweites Mal als Ordner erstellt werden und home.html/suffix.html wird daher nicht zwischengespeichert.

Blockierte Dateiposition im Dateisystem verhindert das Caching von Unterressourcen

Blockierte Dateiposition im Dateisystem verhindert das Caching von Unterressourcen

Wenn Sie in umgekehrter Reihenfolge vorgehen, also erst home.html/suffix.html anfordern, wird suffix.html zunächst unter einem Ordner /home.html zwischengespeichert. Dieser Ordner wird jedoch gelöscht und durch eine Datei home.html ersetzt, wenn Sie später home.html als Ressource anfordern.

Löschen einer Pfadstruktur, wenn ein übergeordnetes Element als Ressource abgerufen wird

Löschen einer Pfadstruktur, wenn ein übergeordnetes Element als Ressource abgerufen wird

Das Ergebnis der Zwischenspeicherung ist also völlig zufällig und hängt von der Reihenfolge der eingehenden Anfragen ab. Was die Sache noch schwieriger macht, ist die Tatsache, dass es normalerweise mehr als einen Dispatcher gibt. Und die Leistung, die Cache-Trefferrate und das Verhalten sind je nach Dispatcher unterschiedlich. Wenn Sie herausfinden möchten, warum Ihre Website nicht reagiert, müssen Sie sicherstellen, dass Sie sich den richtigen Dispatcher mit der misslichen Caching-Reihenfolge ansehen. Wenn Sie sich den Dispatcher ansehen, der – glücklicherweise – ein günstigeres Anfragemuster aufweist, werden Sie bei dem Versuch, das Problem zu finden, scheitern.

Vermeiden in Konflikt stehender URLs

Sie können in Konflikt stehende URLs vermeiden, bei denen ein Ordnername und ein Dateiname im Dateisystem mit demselben Pfad konkurrieren, wenn Sie bei vorhandenem Suffix eine andere Erweiterung für die Ressource verwenden.

Beispiel

  • http://domain.com/home.html

  • http://domain.com/home.dir/suffix.html

Für beide ist eine Zwischenspeicherung problemlos möglich,

Wählen Sie ein dediziertes Erweiterungsverzeichnis („dir“) für eine Ressource, wenn Sie ein Suffix anfordern, oder verzichten Sie komplett auf das Suffix. In seltenen Fällen ist dies nützlich. Und es ist einfach, diese Fälle korrekt zu implementieren. Und genau das werden wir im nächsten Kapitel sehen, wenn es um die Themen Cache-Invalidierung und -Leerung geht.

Nicht zwischenspeicherbare Anfragen

Sehen wir uns eine kurze Zusammenfassung des letzten Kapitels sowie einige weitere Ausnahmen an. Der Dispatcher kann eine URL zwischenspeichern, wenn sie als zwischenspeicherbar konfiguriert ist und es sich um eine GET-Anfrage handelt. Sie kann bei einer der folgenden Ausnahmen nicht zwischengespeichert werden.

Zwischenspeicherbare Anfragen

  • Die Anfrage ist in der Dispatcher-Konfiguration als zwischenspeicherbar konfiguriert
  • Die Anfrage ist eine einfache GET-Anfrage

Nicht zwischenspeicherbare Anfragen oder Antworten

  • Anfrage, die durch die Konfiguration verweigert wird (Pfad, Muster, MIME-Typ)
  • Antworten, die den Header „Dispatcher: no-cache“ zurückgeben
  • Antwort, die den Header „Cache-Control: no-cache|private“ zurückgibt
  • Antwort, die den Header „Pragma: no-cache“ zurückgibt
  • Anfrage mit Abfrageparametern
  • URL ohne Erweiterung
  • URL mit Suffix ohne Erweiterung
  • Antwort, die einen anderen Status-Code als 200 zurückgibt
  • POST-Anfrage