Einführung in die Aktor-Programmierung mit Akka

Werkzeuge Heiko Seeberger  –  5 Kommentare

Akka ist eine mit Scala und Java verwendbare Implementierung des Actor-Modells für die Java Virtual Machine (JVM). Meist wird sie aus der Concurrency-Warte beschrieben. Das hat durchaus seine Richtigkeit, denn schließlich handelt es sich bei Aktoren um eine Form der Nebenläufigkeit auf Basis der Message-Passing-Concurrency. Im Zentrum des Artikels steht jedoch eine andere, ebenso wichtige Eigenschaft des Actor-Modells, und zwar Robustheit, das heißt die Fähigkeit eines Systems, mit Fehlern erfolgreich umzugehen.

Man kann es drehen und wenden, Fehler lassen sich nicht vermeiden. Zum einen ist kein Programm fehlerfrei, auch kein nach allen Regeln der Kunst getestetes. Daher wird es früher oder später zwangsläufig dazu kommen, dass dem Entwickler Exceptions "um die Ohren fliegen". Zum anderen bringt der Trend hin zu verteilten Systemen noch ganz andere Fehlerquellen mit sich: Was passiert, wenn der Strom ausfällt? Oder die Putzkolonne über das Netzwerkkabel stolpert?

Für die Robustheit eines Systems ist es zunächst wichtig, dass es aus Komponenten aufgebaut ist, mit denen sich Fehler lokal eingrenzen lassen. Dadurch wird verhindert, dass eine fehlerhafte Komponente zwangsläufig das gesamte System "herunterzieht", auch wenn dadurch der Service-Level so lange verringert wird, bis sich die fehlerhafte Komponente wieder verwenden lässt oder man auf eine Alternative ausweichen kann.

Lose Kopplung durch asynchrone Nachrichten

Es dürfte rasch klar werden, dass die Komponenten eines robusten Systems lose gekoppelt sein müssen. Schließlich wird jegliches Wissen über den internen Zustand einer anderen Komponente bei einem Fehler dieser ungültig, sodass die abhängige Komponente anfällig für Fehler der anderen wird. Die (ursprüngliche) Objektorientierung antwortet auf diese Herausforderung, indem Objekte ihren Zustand verbergen und miteinander ausschließlich durch den Austausch von Nachrichten kommunizieren können.

Dieser Nachrichtenaustausch ist in objektorientierten Programmiersprachen wie Scala oder Java in Form synchroner Methodenaufrufe realisiert. Synchron bedeutet, dass der Programmfluss des Aufrufers unterbrochen wird, bis die aufgerufene Methode zurückkehrt oder eine Exception wirft. Ganz entscheidend ist, dass für letztere der Aufrufer zuständig ist. Das mag einem zwar ungewohnt vorkommen, jedoch kann man diese Form der Fehlerbehandlung anschaulich damit gleichsetzen, dass jeder Autofahrer jegliche Pannen selbst beheben können muss. Das führt letztlich doch wieder zu einer starken Kopplung zwischen den Objekten.

Um wirklich lose Kopplung zu erreichen, bedarf es daher asynchroner Kommunikation zwischen den Komponenten. Das heißt nichts anderes, als dass Nachrichten im Stil von Fire-and-forget versendet werden, der Programmfluss des Aufrufers also nicht unterbrochen wird, um auf eine Antwort zu warten. Eine solche wird vom Aufgerufenen womöglich später als weitere Nachricht zurückgeschickt. Dieses Prinzip ist bereits im "großen Maßstab" bekannt, etwa bei HTTP oder JMS (Java Message Service), aber es lässt sich auch auf die "Welt der kleinen Objekte" übertragen, und genau das tut das Actor-Modell. Darin sind Aktoren fundamentale Programmbausteine, die ausschließlich über asynchrone Nachrichten miteinander kommunizieren können.