scalatutorial.de

Stufe 6Werkzeuge und Oberflächen · Lektion 31 von 34

Akka und Apache Pekko: Aktoren

Aktualisiert am 24. September 20263 Min. LesezeitSchritt 3/6 dieser Stufe

Akka hat das Aktorenmodell auf der JVM populär gemacht: kleine, unabhängige Einheiten mit eigenem Zustand, die nur über Nachrichten kommunizieren. Seit dem Lizenzwechsel 2022 gibt es mit Apache Pekko einen freien Ableger, der dieselbe Idee unter Apache-Lizenz weiterführt.

Das Aktorenmodell

Zwei Aktoren mit eigenem Postfach tauschen Nachrichten ausAktor KassePostfach ▢ ▢ ▢Zustand: privatAktor LagerPostfach ▢ ▢ ▢Zustand: privatReserviere(3)ReserviertKein geteilter Speicher: Aktoren sprechen nur über Nachrichten miteinander.
Jeder Aktor hat ein Postfach und einen privaten Zustand.

Ein Aktor ist ein Objekt, das

Weil ein Aktor immer nur eine Nachricht gleichzeitig bearbeitet und seinen Zustand mit niemandem teilt, braucht es keine Sperren. Tausende oder Millionen Aktoren können auf wenigen Threads laufen, und sie lassen sich sogar über mehrere Rechner verteilen.

Akka, Lizenz und Pekko

Akka wurde von Jonas Bonér ab 2009 entwickelt; die erste öffentliche Version erschien Anfang 2010. Es wurde zur Grundlage vieler Scala-Systeme und des Play Frameworks. Im September 2022 kündigte die Firma Lightbend an, Akka künftig nicht mehr unter der freien Apache-Lizenz 2.0, sondern unter der Business Source License zu veröffentlichen – für viele kommerzielle Nutzungen wurde damit eine Lizenzgebühr fällig. Nach drei Jahren fällt der jeweilige Code wieder unter die Apache-Lizenz.

Als Antwort entstand Apache Pekko, ein Fork der letzten Apache-lizenzierten Akka-Version. Die Apache Software Foundation hat Pekko im Mai 2024 zum eigenständigen Top-Level-Projekt erklärt. Die Programmierschnittstelle ist weitgehend identisch; beim Umstieg ändern sich vor allem Paketnamen (org.apache.pekko statt akka) und die Namen der Abhängigkeiten.

Einbinden

build.sbtsbt
val pekkoVersion = "1.1.3"
libraryDependencies += "org.apache.pekko" %% "pekko-actor-typed" % pekkoVersion

Die Versionsnummer übernehmen Sie am besten von der Projektseite; sie dient hier nur als Beispiel.

Ein typisierter Aktor

Die moderne Schnittstelle heißt Typed: Der Typ eines Aktors legt fest, welche Nachrichten er versteht. Falsche Nachrichten erkennt so schon der Compiler.

 Scala 3
import org.apache.pekko.actor.typed.{ActorRef, ActorSystem, Behavior}
import org.apache.pekko.actor.typed.scaladsl.Behaviors

object Zaehler:
  enum Befehl:
    case Erhoehen(um: Int)
    case Abfragen(antwortAn: ActorRef[Int])

  import Befehl.*

  def apply(stand: Int = 0): Behavior[Befehl] =
    Behaviors.receiveMessage {
      case Erhoehen(n) =>
        Zaehler(stand + n)                 // neues Verhalten mit neuem Stand
      case Abfragen(an) =>
        an ! stand                          // Antwort schicken
        Behaviors.same
    }

Einige Dinge fallen auf:

Das System starten

 Scala 3
@main def start(): Unit =
  val system: ActorSystem[Zaehler.Befehl] = ActorSystem(Zaehler(), "zaehler")
  system ! Zaehler.Befehl.Erhoehen(5)
  system ! Zaehler.Befehl.Erhoehen(3)

Um eine Antwort zu erhalten, braucht der Absender selbst eine Adresse (ActorRef[Int]). Dafür gibt es das Ask-Muster, das eine Antwort als Future liefert, oder man lässt einen anderen Aktor fragen, der die Antwort als Nachricht bekommt.

Aufsicht: Let it crash

Aktoren bilden eine Hierarchie: Jeder Aktor wird von einem Elternaktor erzeugt und von ihm beaufsichtigt. Wirft ein Aktor eine Ausnahme, entscheidet eine Aufsichtsstrategie, was passiert – etwa neu starten, anhalten oder mit Verzögerung neu starten:

 Scala 3
Behaviors.supervise(Zaehler())
  .onFailure[IllegalStateException](SupervisorStrategy.restart)

Die Philosophie dahinter stammt aus der Sprache Erlang und heißt let it crash: Statt jeden denkbaren Fehler an Ort und Stelle abzufangen, lässt man einen fehlerhaften Aktor abstürzen und startet ihn in einem sauberen Zustand neu.

Mehr als Aktoren

Rund um den Kern gibt es Module für viele Aufgaben:

ModulZweck
StreamsDatenströme mit Gegendruck (backpressure)
HTTPHTTP-Server und -Clients auf Basis von Streams
ClusterAktoren über mehrere Rechner verteilen
PersistenceZustand über Ereignisse speichern (event sourcing)

Wann Aktoren?

Aktoren glänzen bei langlebigen Einheiten mit Zustand, bei vielen gleichzeitigen Verbindungen und bei verteilten Systemen. Für eine einzelne Berechnung, die im Hintergrund läuft und ein Ergebnis liefert, genügt ein Future. Und für einfache Webanwendungen müssen Sie Aktoren gar nicht selbst schreiben – Play nutzt sie im Hintergrund.

Passt dazu