Dienstag, 19. April 2011

Effizientes betreiben von Portalen

Worum geht es?
In diesem Beitrag wird eine Vorgehensweise aufgezeigt, die zum Einen sehr kostengünstig im Unterhalt ist und zum anderen extrem viele User bedienen soll. Dabei versuche ich grundsätzliche Überlegungen anzuwenden, um den Bedarf zu erkennen und auch die Notwendigkeit von dynamischen Inhalten zu identifizieren. Dieser Beitrag verlangt eine gewisse Grundkenntnis der Materie. Sie kann nicht als generelle Lösung angesehen werden, doch bietet sie in den meisten Fällen eine interessante Vorgehensweise. Hier wird ganz klar eine abweichende Struktur von Webseiten mit Content gewählt und dadurch bedingt sie auch eine etwas breitere Wissensbasis in Webentwicklung und Server-Knowledge.

Warum weiss ich etwas darüber?
Ich habe von je her immer mit sehr vielen Daten und deren Bereitstellung im Netz zu tun gehabt. Sei es die Entwicklung von Shoplösungen für über 150.000 Produkte mit mehr als 1.000 Hersteller damals schon mit PHP3 über einem Web-Abrechungstool für einen grossen Internetzugangsprovider, der alleine pro Tag 20 Millionen Datensätze angesammelt hat bis hin zu RapidShare Entertainment, die eine Portalseite bereitstellten auf der täglich 500.000 User die Inhalte nutzen.

Diese Problemstellungen sollte alle sehr kostengünstig erstellt werden und darüber hinaus wollte ich immer wert auch Einfachheit legen. Einfach zu warten, einfach zu skalieren, einfach an Ressourcenbedarf.
Hier half mir sehr gut andere Ansätze zu wählen und das Wissen aus den anderen Bereichen, die manchmal einem reinen Programmierer fehlen, da er zu tief in der Materie gefangen ist.

Die Idee
Es gilt einen Plattform zu schaffen, die von vielen User besucht werden soll. Der Traffic soll im Rahmen bleiben, die Server sollen so unkompliziert wie möglich sein, damit ein schneller Wechsel auf andere Systeme und Provider möglich ist. Die Ausfallsicherheit sollte unterstützt werden und das Ganze soll natürlich sehr schnell beim Endkunden wirken. Die Daten werden in einer Datenbank erfasst und der Enduser kann diese, nach Freigabe ins System durch den Verfasser, lesen.

Grundlegende Gedanken
Es gibt sehr viele herangehensweisen. Oft wird eine Lösung gewählt, dass man eine Sprache wählt, um hier eine Einfachheit zu bringen. Doch diese "Faulheit" wird oft sehr teuer erkauft. So benötigt man viele Server um die dynamischen Skripte ablaufen zu lassen. Unter Umständen erhöht sich das Risiko, durch das Einsetzen der Skriptsprachen oder der Server ist sehr schwer und kann nicht eben mal neu gestartet werden bei einem Problem. Auch hier gibt es endlose Möglichkeiten.

Grundlegende Erklärungen
Was ist das schnellste, was wir im Web liefern können? Klar, die statischen Inhalte. Wenn der Webserver, die Anfrage verarbeitet und analysiert hat, kann er den nächsten Prozess anwerfen. Dabei geht er folgender Massen vor. Ist die Anfrage direkt für mich oder für einen Interpreter? Geht es zu einem Interpreter, weckt er den und sendet die Anfrage weiter. Dieser startet, meist müssen noch einige Variablen gesetzt werden und dann lädt er die Ressourcen ein. Interpretiert diese und startet den Ablauf. Ist nun auch eine Datenbankabfrage involviert, muss auch hier erstmal die Verbindung hergestellt werden. Dabei spielt es keine Rolle ob man persistente Verbindungen oder dergleichen nutzt, grundsätzlich muss die Datenbankserver-Verbindung hergestellt oder die bestehende Verbindung initialisiert werden. Nachdem nun alles abgearbeitet wurde, wird der erzeugte Ausgabetext an den Webserver zurück gegeben. Dieser sendet nun die Daten an den Client. Zeitintensive und Ressourcenintesive Operationen sind somit der Einsatz von Interpreter und andere nachgelagerte Helfersysteme. Das Einfachste ist somit wenn man in der Lage ist nur statische Inhalte, am besten noch in den Arbeitsspeicher ausgelagert auszuliefern.

Grundlegende Analyse des Systems
Hier muss  man sich die Applikation fertig vorstellen. Durschspielen, was erfasst werden soll und was gelesen werden soll. Oft ist es so, dass das Schreiben der Informationen nicht so oft passiert und das Lesen der Daten den Hauptteil der Applikation darstellt. Nun ist die Frage, ob das Erstellen der Applikation aus dynamischen Skripten entstehen sollte? Eigentlich kann man mindestens 2 Teile ausmachen. Einen schreibenden und einen Lesenden, der nicht dynamisch sein muss. Oft wird für das Lesen nicht einmal eine Zugangskontrolle benötigt. Warum sollte man hier nun Skripte einsetzen und Datenbankabfragen erstellen, für immer die gleichen Daten? Man ermittelt als Bestandteile, die auch rein statisch sein könnten. Dabei grob nur nach den Bereichen gehen und nicht schon auf den Inhalt direkt eingehen. Sprachen und zeitgesteuerte Analysen sind auch statisch. Oft sind Teile statisch realisierbar, die man nie vermutet hätte. Ganz klar ist das aber teils mit einem höheren Programmieraufwand verbunden, aber das Result rechtfertigt dies alle mal.

Die Aufteilung und Realisierung
Nachdem wir nun die Aufteilung gemacht habe und uns für eine Skriptsprache entschieden haben, können wir den serverseitigen Teil erstellen. Wir nehmen PHP und erstellen die Skripte zum Erfassen der Datensätze, Administration (Login, Rechte, etc) und zum Erstellen der statischen Inhalte. Unser PHP Skript erzeugt zu keiner Zeit HTML welches zum Arbeiten angezeigt wird. Dieser Vorgang hat auch den enormen Vorteil, dass wir die Serverkomponente beliebig austauschen könne. So könnten man um Performance zu erzeugen, auch die fertige Serverkomponente komplett in C inklusive dem Webserver realsieren und laufen lassen. Die Enduser-Seite erstellen wir in HTML. So kommunizieren unsere unabhängigen System mittels einer API. Auch hier der grosse Vorteil, dass ein Austauschen des Anzeigessystems komplett unabhängig vom Server statt finden kann.

Der Serverteil
Unser Skript erhält mittels API calls die Befehle zum Arbeiten. So reduzieren wir die eigentliche Arbeit auf den Server, denn für den anzeigenden Bereich nutzen wir statische Dateien. Somit realisiert unser Server nur die Anfragen und Antwortet auch sehr kurz mit JavaScript Code. Dieser wird dann im Client interpretiert. Dazu mehr später. Wir implementieren, die Einbindung der Datenbank und für den Administrator eine Verwaltung: User verwalten (anlegen, editieren, löschen), Beiträge verwalten (anlegen, editieren, löschen).
Dazu kommt dann die Loginprüfung und die Beitragsverwaltung für den einzelnen Benutzer.
Eventuell werden einige automatisierte Aktionen benötigt, wie das automatische Freigeben von Beiträgen, diese kann man auch wunderbar implementieren und später entweder per HTTP-Request oder über einen Consolen Aufruf starten. Ein Cronjob einrichten und fertig ist die Sache. Sobald eine Beitrag fertig ist speichert der Server nicht nur in der Datenbank, sondern auch auf eine Datei mit dem Inhalt. Diese Datei ist
in einem speziellen Format, so dass der Client diese per JavaScript Call einlesen kann und den Inhalt interpretieren kann. Hierzu gibt es verschiedene Herangehensweisen. Eine von mir bevorzugte werde ich in späteren Beiträgen erläutern. Vorerst kann könnte man AJAX einsetzen.

Passwortschutz für Verzeichnisse und Dateien
Grundsätzlich sollte man alles einfach halten, dass heisst nicht, dass das Erstellen einfach ist, sondern, dass wenige Komponenten eingesetzt werden und ein schnelles Protieren auf Backup oder Skalierserver möglich ist. Was könnten wir machen, wenn wir Dateien haben die gesichert sein sollten. Hier erstmal ein Mini-Ausflug in HTTPS. Die SSL Verbindung wird zum Server gemacht. Hierbei stellt der Client erstmal die Verbindung her, bevor er preis gibt, was er vom Server möchte. Man tauscht Schlüsselinformationen auf, so dass eine verschlüsselte Verbindung entstehen kann. Nachdem diese Verbindung steht, fragt der Client die Dateien an. Somit ist sogar der Request der Datei samt Pfad verschlüsselt.
Hier ist der Ansatz ausgelagert. Man könnte 2 oder mehr Schlüssel (zufällige alphanumerische Zeichenfolgen erstellen; also wie Login und Passwort). Diese Schlüssel werden wir um Verzeichnisse zu erstellen. Der Server sollte die Indexe nicht listen, aber um sicher zu gehen, legen wir in jedes Verzeichnis einfach eine Dateie ab die vom Server in der Regel interpretiert wird. Diese Datei muss natürlich schon im vorgelagerten Ordner liegen, da sonst der Ordner aufgelistet wird. Genauso kann man auch nur einzelne Dateien schützen, ist jedoch dann nicht so einfach bei einem Wechsel der Schlüssel alle betroffenen Dateien anzupassen. Deshalb empfehle ich einen Ordner, der ist schnell umbenannt. Diese Methode hat gegenüber HTACCESS auch den Vorteil, dass sie Webserver und Dateisystem unabhängig ist. Also ideal zum portieren.

Die Ausgabe, bzw die Enduser Seite
Der Enduser bekommt eine HTML-Seite. Diese wird mittels JavaScript die nötigen Daten holen, aufbereiten und anzeigen. Somit ist die Performance auf den Client ausgelagert. Die Serverkomponente muss nur sicherstellen, dass die Daten geschickt aufbereitet statisch vorliegen. Der Aufbau der eigentlichen Webapplikation erfolgt mittels Clientseitigem Skript und Nachladen von Elementen in den laufenden HTML Code. So muss serverseitig kein Skript angeworfen werden und keine Datenbank durchforstet werden. Die Applikation könnte eine statische Datei einlesen, welche die Kategorien der Beiträge enthält und diese auflisten. Wenn diese Daten angezeigt werden sollen, wird nach dem gleichen Prinzip wiederum eine Datei eingelesen, welche die Inhalte einliest. Der eigntliche Beitrag, kann ebenfalls in einer Datei ausgelagert sein, so dass der Datenaustausch schnell ist, da nur wenige benötigte Daten gesendet werden. Hier ist auch ein grosser Vorteil, dass die Daten keine Design Elemente enhalten und das Design der Seite ansich durch andere Dateien gesteuert wird. Mehrsprachigkeit, kann genauso realisiert werden, da man das komplette Design auch über statische Dateien einlesen kann. Das ganze bringen wir mittels JavaScript (.innerHTML oder Nodes) im Browser zur Anzeige.

Zu beachten und Überlegungen
Bei dieser Methode existieren unter Umständen sehr viele Dateien auf dem Server. Hier sollte man vorher die Masse an Dateien überdenken und ggf die Order gleich in 1.000 Schritte füllen. Bei Datendateien zum Beispiel könnte man pro Ordner immer 1.000 Objekte speichern und die nächsten in tiefer gelegene Ordner.
Also wenn der Datensatz zum Beispiel die ID: 123456789 hat, könnte der Speicherort sein: d/123/456/89/datensatz.js. Auch hier ist anzuraten, dass die Datendatei nicht Bestandteil der ID ist, so hat man gleich einen Container für weitere Infos, wie Screenshots und dergleichen. Der Zugriff einer einzelnen Datei geht immer sehr schnell, das Listing jedoch von Ordner die sehr viele Dateien enthalten, kann unter Umständen auch abbrechen. Mit der tausender-Regel sollten kaum Probleme auftreten, die ist performant genug und bietet auch ausreichend Kapazitäten. Die Serverkomponente wird nun nicht mehr so stark belastet und man kann einige statische Auslieferungsserver betreiben, während der dynamische Server alleine sein kann und unter Umständen auch nicht mal im Internet. So könnte nach dem Erstellen der Dateien, ein anderer Job diese Dateien auf die statischen Server kopieren. Hier hat man zwar eine Verzögerung, jedoch ist diese heutzutage gering. Nachteil ist hierbei, dass sehr viele Dateien im Voraus erstellt werden müssen und vieles ist redundant. Doch da diese durch den Server erstellt werden, sollte dies nicht schwerwiegend sein. Ein manuelles Eingreifen ist nur bedingt noch möglich und mit viel Aufwand verbunden. Aber gerade einige Listen ändern sich sehr selten und da kann man optimieren mit speziellen Cronjobs, die dann die Daten bei Leerlauf oder dergleichen abarbeiten.

Aus der Praxis
Wir haben damals die RapidGames.com und die dazugehörigen RapidMusic, RapidMovies nach dem gleichen Prinzip erstellt. Ein Server im Office war für die dynamischen Inhalte zuständig und die erzeugten Dateien wurden auf die statischen Server verteilt. Das lief so hervorragend, dass unser statischer Backserver eigentlich nie zum Einsatz kam. Auch ein Loadbalancing war nie nötig, bis zu dem Tag, an dem scheinbar unser Provider doch mehr Kunden aufnahm als verkraftbar. Wir haben damals einen statischen Webhosting Vertrag für ca. 15 CHF gehabt und damit auch 500.000 User pro Tag versorgt. Günstiger geht es kaum noch. Zumal die Lösung durch eine einfache index.html Datei sofort skalierbar war, die dann Anfragen an andere statische Server hätte weiterleiten können. Nicht mal Zugriff auf den Server ist dafür nötig. Sollte der Andrang so gross sein, dass selbst die Hits zuviel sind, kann man DNS Load-Balancing einsetzen. Einfach weitere IPs zum Domain-Eintrag ergänzen. Keep it simple. Klar dass dies nicht immer die saubersten Lösungen sind, aber bis her immer die unkompliziertesten und effektivsten. Man sollte sich immer Out-of-the-Box denken und sich die Mittel ansehen und sich fragen, wie kann ich mit den bestehenden Mittel das Ergebnis erreichen?

Saso Nikolov

Keine Kommentare:

Kommentar veröffentlichen