Architekturen

Was ist eine Webanwendung?

Eine Webanwendung ist ein client-server basiertes, betriebssystemunabhängiges Anwendungsprogramm, das meist im Browser ausgeführt wird. Abhängig vom Architekturmuster einer Webanwendung können die Geschäftslogik und die Datenverarbeitung weitgehend auf dem Server (thin-client) oder im Client erfolgen (fat-client). Im Kontext der "mobilen Revolution" wird der Begriff zunehmend generalisiert verwendet für Software, die sich zum maßgeblichen Teil auf HTTP-APIs stützt.

Grafik: Schematischer Datenfluss in einer Webanwendung
Quelle: Gerd Franke, CC BY-SA 3.0

Aufbau einer Webanwendung

Die Architektur von Webanwendungen kann sehr unterschiedlich sein. Generell ist aber festzuhalten, dass es aufgrund der Zustandslosigkeit des HTTP-Protokolls meist eine Persistenzschicht (Datenhaltung), Anwendungsschicht (Funktionslogik) und Präsenationsschicht gibt (3-Layer Architektur). Die server- oder clientseitige Implementierung dieses Drei-Schichten Modells ist häufig aber unterschiedlich implementiert.

Grafik: Klassische 3-Layer Architektur einer Webanwendung (Thin-Client)
Quelle: Microsoft Application Architecture Guide, 2nd Edition

Mit dem starken Zuwachs im Bereich von Virtualisierung und Cloud Computing verschwimmt das „klassische“ Architekturmuster von Webanwendungen zunehmend. Aktuelle Anwendungen werden zunehmend in kleine Komponenten gekapselt, die in einer verteilten Infrastruktur isolierte Aufgaben wahrnehmen und über webbasierte APIs miteinander kommunizieren (siehe auch den Abschnitt zu DevOps). Man spricht in diesem Fall von microservices-orientierten Architekturen.

Weiterführende Informationen:

Info
Für die folgenden Aufgaben sollten Sie Docker CE auf ihrem Rechner installiert haben.

LAMP Stack

Der LAMP-Softwarestack gehört zu den traditionellen Architekturen zur Bereitstellung von Webanwendungen. Das Akronym LAMP steht für eine Kombination von Linux (Betriebssystem), Apache (Webserver), MySQL (Datenhaltung) und PHP (Programmiersprache, Anwendung). Das Softwarepaket besteht vollständig aus quelloffenen Komponenten und ist bereits seit den 1990iger Jahren etabliert. In der klassischen Architektur des LAMP-Stack wird die Skriptsprache PHP als Modul in den Webserver eingebettet. HTTP-Anfragen an den Webserver werden von diesem - je nach Dateiendung - entweder direkt ausgeliefert oder zur dynamischen Weiterverarbeitung an den PHP-Interpreter überwiesen. Zugriffe auf eine Datenbank erfolgen wiederum durch den PHP-Interpreter.

Grafik: Ursprüngliche Komponenten des LAMP-Stacks
Quelle: Karsten Adam, LAMP Stack Komponenten

Einigen Statistiken zufolge wird die Kombination von Apache-Webserver und PHP nach wie vor am häufigsten zur Erstellung dynamischer Webanwendungen eingesetzt (W3Tech: Apache Usage, W3Tech: PHP Usage). Gründe hierfür sind die übergreifende Verfügbarkeit dieser Pakete, die hohe Dokumentationsdichte, die einfache Erlernbarkeit von PHP sowie die Stabilität des Gesamtpakets.

In jüngerer Zeit werden Komponenten der traditionellen LAMP-Architektur zuweilen mit neueren Produkten ersetzt. Im Bereich des Webservers kommt häufiger nginx zum Einsatz, im Bereich der Datenbank vor allem MariaDB (seit dem Erwerb von MySQL durch die Firma Oracle). Wird nginx ("Engine X") als Webserver eingesetzt spricht man zuweilen auch von einem LEMP-Stack.

Übungen mit dem LAMP-Stack

Folgende Aufgaben wollen wir gemeinsam durchführen und besprechen:

(1) Starten Sie mit den Befehlen unten einen LAMP Docker-Container und loggen Sie sich im Container ein.
(2) Führen Sie mit Atom einen GET-Request auf den Webserver im Container aus und analysieren Sie die Antwort.
(3) Rufen Sie die index.php Datei im Browser auf und sehen Sie sich die Ausgabe von phpinfo() an.
(4) Analysieren Sie das Webserver-Logfile in /var/log/apache2/access.log
(5) Loggen Sie sich auf der Konsole des MySQL-Servers ein und lassen Sie sich alle Datenbanken anzeigen.

Befehl zum starten eines LAMP Docker Containers (Linux, Windows, MAC):

docker run -i -t --rm -p 8090:80 --name lamp fauria/lamp

Auf dem Rasperry Pi verwenden wir aufgrund der abweichenden Prozessor-Architektur (arm Prozessor) ein anderes Docker LAMP Image:

docker run -i -t --rm -p 8090:80 --name lamp wednus/rpi-lamp

Einloggen in den LAMP Docker Container:

docker exec -e TERM=xterm -i -t lamp bash

MEAN/MEVN Stack

Die Anforderungen an moderne Webanwendungen beginnen sich zunehmend zu verändern. Ausgelöst durch die schnellen Entwicklungen im Bereich von Smartphones und Tablets verschwimmen die Grenzen zwischen Desktop- und Webanwendungen zunehmend. Es spielt immer weniger eine Rolle, ob eine Anwendung nativ installiert ist oder webgetrieben auf einem Gerät läuft. Aus Nutzersicht fühlt sich der Umgang mit der Anwendung häufig gleich an. Die meisten mobilen Anwendungen verwenden zudem HTTP-basierte Schnittstellen und Web-APIs. Mit dieser Entwicklung geht eine "Renaissance" der Programmiersprache JavaScript einher. Während JavaScript in den Anfängen des Web vor allem in der Präsentationsschicht einer Webanwendung zum Einsatz kam, ist durch die Entwicklung von node.js ein übergreifender Einsatz von JavaScript in allen Architekturschichten möglich geworden.

Neben node.js haben sich (maßgeblich vorangetrieben durch die großen Internetkonzerne) leistungsstarke Full-Stack JavaScript Frameworks entwickelt (zum Beispiel Angular, Vue oder React). Diese Entwicklung ist bedeutsam, da somit Anwendungen "aus einem Guß" nativ in JavaScript realisiert werden können. Neben den traditionellen LAMP-Stack treten also zunehmend JavaScript-getriebene Laufzeitumgebungen.

Eine inzwischen verbreitete Form eines solchen JavaScript-Webstacks ist der MEAN-Stack. Das Acronym steht für MongoDB (dokumentenorientierte Datenbank), Express (Backend-Framework), Angular (Frontend-Framework) und Node (serverseitige Laufzeitumgebung). Als Austausch- und Speicherungsformat für Daten wird innerhalb des MEAN-Stacks JSON eingesetzt. Je nach verwendetem JavaScript-Framework finden sich manchmal auch alternative Bezeichnungen wie MEVN (das V steht für Vue.js) oder VENOM (Vue, Express, Node, MongoDB).

Grafik: Komponenten des MEAN Stack
Quelle: Andrew Morgan, The Modern Application Stack

Die Komponenten dieser JavaScript-Stacks kommunizieren HTTP-basiert über Schnittstellen miteinander. Es liegt bei der Verwendung eines solchen Stacks ganz in der Entscheidung des Entwicklungsteams, ob die Anwendung eher client- oder serverseitig oder sogar paritätisch implementiert wird. Die Technologie lässt aufgrund der durchgängigen Verwendung von JavaScript sehr große Spielräume.

Übungen mit dem VENOM-Stack

Folgende Aufgaben wollen wir gemeinsam durchführen und besprechen:

(1) Starten Sie mit den unten genannten Befehlen einen Docker VENoM Stack.
(2) Rufen Sie die Applikation auf und legen Sie unter http://localhost:8080/posts einige Post an.
(3) Öffnen Sie in Atom das VENoM-Docker Verzeichnis und analysieren Sie die einzelnen Komponenten des Frameworks.
(4) Loggen Sie sich mit den unten aufgeführten Befehlen in den MongoDB Container und dort in die Datenbank-Konsole ein. Rufen Sie die Posts-Datenbank auf und lassen Sie sich die über die Webanwendung angelegten Datensätze anzeigen. (5) Legen Sie mit Atom/Rester mittels eines POST Requests unter Verwendung der API unter http://localhost:8081/posts einen neuen Blogeintrag an.

Befehle zum Starten des VENoM-Stacks:

git clone https://github.com/jamesaud/VENoM-Docker (im Nutzerverzeichnis clonen)
cd VENoM-Docker/
docker-compose build && docker-compose up

Einloggen in den MongoDB-Container:

docker exec -e TERM=xterm -i -t venom-docker_server-database_1 bash

In der MongoDB Datenbank-Konsole einloggen und Datensätze anzeigen:

mongo
show dbs
use posts
show collections
db.posts.find();

POST Request zum Anlegen neuer Posts über die HTTP API:

POST /posts HTTP/1.1
Host: localhost:8081
Content-type: application/json

{
    "title": "FooBar",
    "description": "Post created via REST"
}

Weiterführende Informationen:

Skalierbarkeit und verteilte Systeme

Insbesondere im Bereich hochperformanter Webanwendungen mit Millionen Anfragen pro Tag ist es notwendig, die Systemlast zu verteilen und Anwendungen zu skalieren. Eine Anwendung kann vertikal (scale-up) oder horizontal (scale-out) skaliert werden. Bei der vertikalen Skalierung werden einem Rechner mehr Ressourcen hinzugefügt (z.B. mehr Arbeitsspeicher oder mehr CPUs). Bei der horizontalen Skalierung werden zusätzliche Rechner zum Gesamtsystem hinzugefügt, wobei die Anwendung weiterhin einen Einstiegspunkt hat. Vertikale Skalierbarkeit hat eine physische Obergrenze (ein Rechner lässt sich nur bis zu einem bestimmten Grad aufrüsten), horizontale Skalierbarkeit kann softwaretechnische Grenzen haben (z.B. wenn sich eine Software nicht gut parallelisieren lässt).

In der Praxis sind häufig Mischformen im Einsatz. Hierbei werden spezifische Dienste einer Webanwendung von einander getrennt und als separate Systeme betrieben (z.B. Webserver und Datenbankserver). In einem solchen Fall spricht man von einem verteilten System. Gleichzeitig können die Komponenten des verteilten Systems (meist durch Virtualisierung) im Betrieb mit mehr Ressourcen ausgestattet werden. Bei Lastspitzen können die Webserver in einer virtualisierten verteilten Umgebung bspw. on-the-fly mehr Arbeitsspeicher zugewiesen bekommen. Daneben können auch spezialisierte Rechnerknoten (bspw. ein Load Balancer) eingesetzt werden.

Grafik: Verteilte, cloudbasierte VM-Infrastruktur der Regesta Imperii (n-tier)
Quelle: Torsten Schrade

Grundsätzlich gilt die Regel, je verteilter das System, umso komplexer das Deployment bzw. das Roll-Out einer Webanwendung. Verteilte Systeme werden durch die großen Fortschritte im Bereich von Anwendungscontainern immer mehr zum Standard.

Weiterführende Informationen:

WS 2019/20 – Webbasierte Forschungsapplikationen für die Geisteswissenschaften CC BY-NC-SA 4.0