---
title: "Skalierbares Anwendungsdesign ohne Magie"
date: 2020-05-11
language: de
topics: ["cloud", "data", "design", "howto", "iOS", "lemmid-store", "programming", "server", "smartphone", "web", "webdesign"]
source_url: https://willem.com/de/2020-05-11_skalierbares-anwendungsdesign-ohne-magie/
---
# Skalierbares Anwendungsdesign ohne Magie

*Die Rechenleistung der Clients nutzen, für hohe Leistung bei vielen Nutzern*

> Für die Online-Bestell-App, die ich gerade baue, brauchte ich eine skalierbare Backend-Infrastruktur, die viele gleichzeitige Nutzer verkraftet. Skalierbarkeit gilt als schwieriges Problem. Oft wird sie dargestellt, als wäre sie Magie, die nur millionenschwere Firmen mit geheimen Werkzeugen beherrschen. Aber so etwas wie Magie gibt es nicht. Oder etwa doch?

## Was ist Skalierbarkeit?

Wenn du eine App baust, fängst du wahrscheinlich ganz klein an. Nur ein paar Nutzer (oder nur du, der Entwickler) arbeiten mit der App. Alles ist bestens: Die App läuft gut und schnell, und es gibt keine Probleme. *(Genieße dieses Gefühl!)*

![Ein Nutzer arbeitet mit einem Server](https://willem.com/global/images/02bda6e71f78.webp)

In diesem frühen, kleinen Szenario kommt der einzelne Server, auf dem die App läuft, mit dem Nutzer gut zurecht. Jedes Mal, wenn der Nutzer etwas antippt oder klickt, erledigt der Server ein bisschen Arbeit. Wenn du gut programmierst, lässt sich diese Arbeit in effiziente Häppchen packen. Das ist ein typisches Beispiel für eine monolithische_Software-Architektur (https://willem.com/en/2020-03-03_monolithic-vs-microservices-software-architecture/).

Auch wenn dein Projekt oder deine App eher klein ist, kann irgendwann ein Schwung neuer Nutzer kommen. Wenn dein System hohe Lasten nicht tragen kann, geht es sehr wahrscheinlich schief.

![Server unter hoher Last, der mehrere gleichzeitige Nutzer bedient](https://willem.com/global/images/0983daa5d3fa.webp)

Bei einem_monolithischen_App-Design (https://willem.com/en/2020-03-03_monolithic-vs-microservices-software-architecture/) sprechen alle Nutzer mit demselben Server. Alles, was sie tun (Tippen, Klicken, Eingaben), verarbeitet dieser Server. Steigt die Last, muss er viel härter arbeiten. Irgendwann merkst du, dass alles langsamer wird, weil der Server mit der Arbeit nicht mehr hinterherkommt. Das heißt, deine App skaliert nicht gut. Anders gesagt: Du hast ein Problem.

### Ausfall im entscheidenden Moment

Schlechte Skalierung ist ein großes Problem, das niemand unterschätzen sollte. Die meisten Apps (und ihre Geschäftsmodelle) leben von großen Mengen bei geringem Ertrag pro Nutzer. Soll deine App ein (finanzieller) Erfolg werden, muss sie auch bei plötzlich steigendem Traffic gut laufen. Stürzt deine App ab, wenn sie plötzlich die Aufmerksamkeit der Massen bekommt, verpasst du eine Chance auf (organisches) Wachstum, die vielleicht nie wiederkommt.

Skalierbarkeit einzuplanen ist ein bisschen wie Anschnallen in deiner selbst gebauten Rakete mit experimentellen Triebwerken. Du weißt nie genau, wann sie zündet, aber wenn es so weit ist, solltest du fest sitzen (und den Flug genießen!).

## Gängige Wege zu mehr Skalierbarkeit

Zum Bau skalierbarer Apps ist schon viel geschrieben worden. Gängige Wege sind:

- **Mehr Serverkapazität**: mehr Arbeitsspeicher, CPU-Leistung, Speicherplatz. Das funktioniert, bringt dich aber nur "bis zu einem gewissen Punkt".

- **Mehr Server**: Statt eines einzelnen Servers als Flaschenhals kannst du weitere Server einsetzen, die sich die Arbeit teilen. Das ist leichter gesagt als getan, denn du brauchst Load Balancing und gemeinsame Daten, und das kann knifflig sein (zum Beispiel: Wenn du einen Bestellzähler hast und zwei Server zählen, wer bestimmt dann die nächste Nummer?).

- **Den Programmcode optimieren**: Nimm die richtigen Werkzeuge! Wähle eine leistungsorientierte Programmiersprache und passende Serversoftware. Schau dir funktionale Programmierung an, damit dein Code asynchron auf mehreren Kernen laufen kann. Mach ihn zustandslos. Miss die Leistung. Optimiere. Jede Millisekunde, die du bei einer einzelnen Anfrage gewinnst, summiert sich schnell, wenn viel los ist.

- **Die Datenbank nutzen und vom Anwendungsserver trennen**: Wenn du eine Datenbank einsetzt, hol alles aus ihr heraus! Nutze Query-Caching, Indizes und Suchfunktionen. Die meisten Datenbanken bieten erprobte Clustering-Optionen. Erfinde das Rad nicht neu: Andere haben es längst perfektioniert.

Am besten ziehst du alle diese Möglichkeiten in Betracht. Auch wenn du nicht sofort weitere Server hinzufügst, solltest du deinen Code so schreiben, dass es später möglich ist. Benchmarks und Optimierung gehören fest zu deiner Arbeit und sind kein nachträglicher Einfall.

## Die Rechenleistung der Clients nutzen

Wenn du alle gängigen Wege durchdacht hast, gibt es noch "eine Sache".

![Smartphones (und Tablets, PCs) sind im Lauf der Jahre immer leistungsfähiger geworden (Bild: Apple)](https://willem.com/global/images/e4d580ef3ee0.webp)

Daran denken nur wenige Entwickler: Das Skalierungsproblem enthält die Lösung! Bei 1000 Nutzern hast du 1000 Computer mit leistungsstarken CPUs und viel Speicher. Moderne Computer und Smartphones können immer mehr.

Du musst nur einen Weg finden, die Rechenleistung des Clients anzuzapfen. Das klingt vielleicht gemein (die Rechenleistung des Nutzers zu benutzen), aber es verschafft ihm ein viel besseres, schnelleres Erlebnis. Das Beste daran: *Seine* Rechenleistung wird für *sein* tolles Erlebnis verwendet, und dein Skalierungsproblem löst sich ganz nebenbei als glücklicher Nebeneffekt.

![Das Gerät des Nutzers einspannen: Sein Telefon übernimmt den Großteil der Arbeit, die ursprünglich der Server erledigt hat](https://willem.com/global/images/3b24ef5be7d6.webp)

Wenn du die Rechenleistung des Nutzers einsetzt, hat der Server weniger zu tun. Statt dass der Server *jedes* Mal ein bisschen arbeitet, wenn der Nutzer tippt oder klickt, erledigt das nun größtenteils das Gerät des Nutzers. Das ist effizienter, als über WLAN oder 4G mit einem Server zu sprechen (spart Akku und Zeit).

Das erfordert ein wenig Umdenken, aber das Prinzip lässt sich auch auf Webanwendungen übertragen. Statt alles auf dem Server zu erledigen, kannst du viel Arbeit auf den Client verlagern. Suchen, Stöbern, Filtern, Navigieren und sogar das Befüllen eines Warenkorbs funktionieren mit clientseitigem JavaScript. Du - lieber Entwickler - musst Wege finden, das umzusetzen. Aber wenn es dir gelingt, hast du nahezu unbegrenzte Skalierbarkeit in der Hand.

## In der Praxis: die Bestell-App für Essen

Für die Bestell-App_für_Essen (https://willem.com/en/2020-05-01_designing-an-interface-for-a-food-ordering-page/) habe ich nach Wegen gesucht, die Rechenleistung der Clients für hohe Leistung zu nutzen. Die größte Herausforderung bei Liefer- und Abholbestellungen ist, dass sie zur Essenszeit ihren Höhepunkt erreichen. Viele Leute nutzen die App ungefähr gleichzeitig. Das ist ein Rezept für Skalierungsprobleme.

![Essen online bestellen](https://willem.com/global/images/d08b396a761f.webp)

Wenn du an Essensbestellungen im Netz denkst, kannst du den ganzen Ablauf in kleinere Schritte zerlegen:

- **1) Die Seite öffnen**: App oder Seite laden

- **2) Die Auswahl durchstöbern**: Produkte auflisten, Beschreibungen, Preise, Fotos usw. ansehen

- **3) Gezielt suchen**: nach Kategorie, Produktname usw.

- **4) Ein Produkt auswählen**: zur Bestellung hinzufügen

- **5) Ein Produkt anpassen**: Sauce, Beilage, Topping usw. wählen

- **6) Deine Daten eingeben**: Name, Telefonnummer, Lieferdetails usw.

- **7) Die Bestellung bezahlen**: Zahlungsmethode wählen, Verbindung zu deiner Bank oder Kreditkarte

- **8) Die App schließen**

Die meisten dieser Schritte lassen sich so programmieren, dass der Server *nicht nötig* ist. Nur Schritt 1 (**Laden**) und Schritt 7 (**Bezahlen**) brauchen Kontakt zur Backend-Infrastruktur. Wichtig: Die Schritte 2 bis 5 wiederholen sich oft, denn Nutzer legen meist mehrere Produkte in ihre Bestellung.

![Die restliche Serverlast aufteilen: Produkte ausliefern und Bestellungen verarbeiten](https://willem.com/global/images/1d2831da7a52.webp)

Teilt man die restliche Arbeit nach Aufgaben auf, lässt sich die Software weiter optimieren. Der **"Produktserver"** ist darauf ausgelegt, statische Inhalte (Texte, Preise, Bilder) auszuliefern, mit denen der Client das Stöbern aufbaut. Diesen Servertyp kannst du stark optimieren, mit Caching, HTTP/2, Komprimierung usw.

Nur Nutzer, die ihre Bestellung abschließen, werden zum **"Bestellserver"** weitergeleitet, der die Zahlung abwickelt. Er verbindet sich mit dem Zahlungsanbieter. Ist eine Zahlung abgeschlossen, meldet sich der Zahlungsanbieter beim Bestellserver und teilt den Zahlungsstatus mit. Das willst du vollständig (serverseitig) selbst in der Hand haben, so wie ich es getan habe, als ich früher ein_Zahlungssystem_entworfen_habe (https://willem.com/en/2020-03-25_designing-and-implementing-a-micro-payment-system/). Die Restlast auf dem "Bestellserver" ist viel kleiner, weil nicht jeder bestellt und ein Großteil der Bestellverarbeitung im Hintergrund laufen kann.

![Viel Traffic bewältigen durch verteiltes Rechnen](https://willem.com/global/images/4c106f30570a.webp)

Für maximale Skalierbarkeit kannst du ganz einfach mehrere Server einsetzen (oder ein Content Delivery Network nutzen), die die statischen Inhalte ausliefern. Für die Bestell-App verwende ich *pro Restaurant* einen eigenen "Produktserver". Das sorgt für hohe Leistung und kurze Ladezeiten. Die Leute merken, dass es schnell ist, manche können nicht glauben, dass das Webtechnik ist. Es ist *fast* magisch.

## Fazit

Eine skalierbare Anwendung zu entwerfen geht ohne Magie und ohne Millionenbudget. Du musst nur gründlich nachdenken und die Chancen nutzen, die zusammen mit den Herausforderungen auftauchen.

Das Problem enthält die Lösung. Viele gleichzeitige Nutzer sind nicht nur ein Problem: Sie bringen ihre eigene Rechenleistung mit, und die ist die Lösung! Du musst sie nur nutzen.
