Parallelität und Fehlertoleranz leicht gemacht: Ein Akka-Tutorial mit Beispielen
Veröffentlicht: 2022-03-11Die Herausforderung
Nebenläufige Programme zu schreiben ist schwierig. Der Umgang mit Threads, Sperren, Race-Bedingungen usw. ist sehr fehleranfällig und kann zu Code führen, der schwer zu lesen, zu testen und zu warten ist.
Viele verzichten daher lieber ganz auf Multithreading. Stattdessen verwenden sie ausschließlich Single-Thread-Prozesse und verlassen sich auf externe Dienste (wie Datenbanken, Warteschlangen usw.), um alle erforderlichen gleichzeitigen oder asynchronen Vorgänge zu verarbeiten. Während dieser Ansatz in einigen Fällen eine legitime Alternative ist, gibt es viele Szenarien, in denen er einfach keine praktikable Option ist. Viele Echtzeitsysteme – wie Handels- oder Bankanwendungen oder Echtzeitspiele – haben nicht den Luxus, auf den Abschluss eines Singlethread-Prozesses zu warten (sie brauchen die Antwort jetzt!). Andere Systeme sind so rechen- oder ressourcenintensiv, dass ihre Ausführung übermäßig viel Zeit (in einigen Fällen Stunden oder sogar Tage) in Anspruch nehmen würde, ohne Parallelisierung in ihren Code einzuführen.
Ein ziemlich verbreiteter Single-Thread-Ansatz (der beispielsweise in der Node.js-Welt weit verbreitet ist) besteht darin, ein ereignisbasiertes, nicht blockierendes Paradigma zu verwenden. Obwohl dies die Leistung verbessert, indem Kontextwechsel, Sperren und Blockierungen vermieden werden, werden die Probleme der gleichzeitigen Verwendung mehrerer Prozessoren immer noch nicht behoben (dazu müssten mehrere unabhängige Prozesse gestartet und zwischen ihnen koordiniert werden).
Bedeutet das, dass Sie keine andere Wahl haben, als tief in die Eingeweide von Threads, Locks und Race Conditions zu reisen, um eine gleichzeitige Anwendung zu erstellen?
Dank des Akka-Frameworks lautet die Antwort nein. Dieses Tutorial stellt Akka-Beispiele vor und untersucht, wie es die Implementierung gleichzeitiger, verteilter Anwendungen erleichtert und vereinfacht.
Was ist das Akka-Framework?
Akka ist ein Toolkit und eine Laufzeitumgebung zum Erstellen hochgradig gleichzeitiger, verteilter und fehlertoleranter Anwendungen auf der JVM. Akka ist in Scala geschrieben, wobei Sprachbindungen sowohl für Scala als auch für Java bereitgestellt werden.
Akkas Ansatz zum Umgang mit Nebenläufigkeit basiert auf dem Akteurmodell. In einem akteurbasierten System ist alles ein Akteur, ähnlich wie im objektorientierten Design alles ein Objekt ist. Ein entscheidender Unterschied – der für unsere Diskussion besonders relevant ist – besteht jedoch darin, dass das Akteursmodell speziell entworfen und konstruiert wurde, um als gleichzeitiges Modell zu dienen, während das objektorientierte Modell dies nicht ist. Genauer gesagt, in einem Scala-Akteurssystem interagieren Akteure und tauschen Informationen aus, ohne dass eine Sequentialität vorausgesetzt wird. Der Mechanismus, durch den Akteure Informationen miteinander teilen und sich gegenseitig Aufgaben stellen, ist die Nachrichtenübermittlung.
Akka erstellt eine Schicht zwischen den Akteuren und dem zugrunde liegenden System, sodass Akteure lediglich Nachrichten verarbeiten müssen. Die gesamte Komplexität des Erstellens und Planens von Threads, des Empfangens und Versendens von Nachrichten sowie des Umgangs mit Race-Bedingungen und Synchronisierung wird zur transparenten Handhabung auf das Framework verlagert.
Akka hält sich strikt an das The Reactive Manifesto. Reaktive Anwendungen zielen darauf ab, herkömmliche Multithread-Anwendungen durch eine Architektur zu ersetzen, die eine oder mehrere der folgenden Anforderungen erfüllt:
- Ereignisgesteuert. Mit Actors kann man Code schreiben, der Anforderungen asynchron verarbeitet und ausschließlich nicht blockierende Operationen verwendet.
- Skalierbar. In Akka ist das Hinzufügen von Knoten möglich, ohne den Code ändern zu müssen, sowohl dank der Nachrichtenweitergabe als auch der Standorttransparenz.
- Robust. Jede Anwendung wird auf Fehler stoßen und irgendwann fehlschlagen. Akka bietet „Überwachungs“-Strategien (Fehlertoleranz), um ein Selbstheilungssystem zu ermöglichen.
- Reaktionsschnell. Viele der heutigen Hochleistungs- und Rapid-Response-Anwendungen müssen dem Benutzer ein schnelles Feedback geben und müssen daher extrem zeitnah auf Ereignisse reagieren. Die blockierungsfreie, nachrichtenbasierte Strategie von Akka hilft dabei.
Was ist ein Schauspieler in Akka?
Ein Akteur ist im Wesentlichen nichts anderes als ein Objekt, das Nachrichten empfängt und Maßnahmen ergreift, um sie zu verarbeiten. Es ist von der Quelle der Nachricht entkoppelt und seine einzige Verantwortung besteht darin, die Art der empfangenen Nachricht richtig zu erkennen und entsprechende Maßnahmen zu ergreifen.
Beim Empfang einer Nachricht kann ein Akteur eine oder mehrere der folgenden Aktionen ausführen:
- Einige Operationen selbst ausführen (z. B. Berechnungen durchführen, Daten persistent speichern, einen externen Webdienst aufrufen usw.)
- Leiten Sie die Nachricht oder eine abgeleitete Nachricht an einen anderen Akteur weiter
- Instanziieren Sie einen neuen Akteur und leiten Sie die Nachricht an ihn weiter
Alternativ kann sich der Akteur dafür entscheiden, die Nachricht vollständig zu ignorieren (dh er kann sich für Untätigkeit entscheiden), wenn er dies für angemessen hält.
Um einen Akteur zu implementieren, ist es notwendig, die Eigenschaft akka.actor.Actor zu erweitern und die Methode Receive zu implementieren. Die Receive-Methode eines Akteurs wird (von Akka) aufgerufen, wenn eine Nachricht an diesen Akteur gesendet wird. Seine typische Implementierung besteht aus dem Musterabgleich, wie im folgenden Akka-Beispiel gezeigt, um den Nachrichtentyp zu identifizieren und entsprechend zu reagieren:
import akka.actor.Actor import akka.actor.Props import akka.event.Logging class MyActor extends Actor { def receive = { case value: String => doSomething(value) case _ => println("received unknown message") } }
Der Musterabgleich ist eine relativ elegante Technik zur Verarbeitung von Nachrichten, die tendenziell „saubereren“ und einfacher zu navigierenden Code erzeugt als eine vergleichbare Implementierung auf der Grundlage von Rückrufen. Betrachten Sie zum Beispiel eine vereinfachte HTTP-Request/Response-Implementierung.
Lassen Sie uns dies zunächst mit einem Callback-basierten Paradigma in JavaScript implementieren:
route(url, function(request){ var query = buildQuery(request); dbCall(query, function(dbResponse){ var wsRequest = buildWebServiceRequest(dbResponse); wsCall(wsRequest, function(wsResponse) { sendReply(wsResponse); }); }); });
Vergleichen wir dies nun mit einer auf Musterabgleich basierenden Implementierung:
msg match { case HttpRequest(request) => { val query = buildQuery(request) dbCall(query) } case DbResponse(dbResponse) => { var wsRequest = buildWebServiceRequest(dbResponse); wsCall(dbResponse) } case WsResponse(wsResponse) => sendReply(wsResponse) }
Callback-basierter JavaScript-Code ist zwar kompakt, aber sicherlich schwieriger zu lesen und zu navigieren. Im Vergleich dazu macht der auf Pattern-Matching basierende Code direkter ersichtlich, welche Fälle betrachtet und wie jeder behandelt wird.
Das Akteurssystem
Ein komplexes Problem zu nehmen und es rekursiv in kleinere Teilprobleme aufzuteilen, ist im Allgemeinen eine solide Problemlösungstechnik. Dieser Ansatz kann in der Informatik besonders vorteilhaft sein (in Übereinstimmung mit dem Single-Responsibility-Prinzip), da er dazu neigt, sauberen, modularisierten Code mit wenig oder keiner Redundanz zu liefern, der relativ einfach zu warten ist.
In einem akteurbasierten Design erleichtert die Verwendung dieser Technik die logische Organisation von Akteuren in einer hierarchischen Struktur, die als Akteursystem bekannt ist. Das Akteurssystem stellt die Infrastruktur bereit, über die Akteure miteinander interagieren.
In Akka ist die einzige Möglichkeit, mit einem Akteur zu kommunizieren, über eine ActorRef
. Eine ActorRef
stellt einen Verweis auf einen Akteur dar, der andere Objekte daran hindert, direkt auf die Interna und den Zustand dieses Akteurs zuzugreifen oder diese zu manipulieren. Nachrichten können über eine ActorRef
unter Verwendung eines der folgenden Syntaxprotokolle an einen Akteur gesendet werden:
-
!
(„Tell“) – sendet die Nachricht und kehrt sofort zurück -
?
(„ask“) – sendet die Nachricht und gibt ein Future zurück, das eine mögliche Antwort darstellt
Jeder Akteur hat eine Mailbox, an die seine eingehenden Nachrichten zugestellt werden. Es stehen mehrere Postfachimplementierungen zur Auswahl, wobei die Standardimplementierung FIFO ist.
Ein Akteur enthält viele Instanzvariablen, um den Zustand beizubehalten, während mehrere Nachrichten verarbeitet werden. Akka stellt sicher, dass jede Instanz eines Akteurs in einem eigenen leichten Thread ausgeführt wird und dass Nachrichten einzeln verarbeitet werden. Auf diese Weise kann der Zustand jedes Akteurs zuverlässig aufrechterhalten werden, ohne dass sich der Entwickler explizit um Synchronisation oder Wettlaufbedingungen kümmern muss.
Jeder Akteur erhält die folgenden nützlichen Informationen zur Erfüllung seiner Aufgaben über die Akka Actor API:
-
sender
: eineActorRef
auf den Absender der gerade verarbeiteten Nachricht -
context
: Informationen und Methoden, die sich auf den Kontext beziehen, in dem der Akteur ausgeführt wird (enthält beispielsweise eineactorOf
-Methode zum Instanziieren eines neuen Akteurs) -
supervisionStrategy
: Definiert die Strategie, die zur Wiederherstellung nach Fehlern verwendet werden soll -
self
: dieActorRef
für den Akteur selbst
Um diese Lernprogramme zusammenzufassen, betrachten wir ein einfaches Beispiel für das Zählen der Anzahl der Wörter in einer Textdatei.
Für unser Akka-Beispiel zerlegen wir das Problem in zwei Teilaufgaben; nämlich (1) eine „Kind“-Aufgabe zum Zählen der Anzahl von Wörtern in einer einzelnen Zeile und (2) eine „Eltern“-Aufgabe zum Summieren dieser Wortzählungen pro Zeile, um die Gesamtzahl von Wörtern in der Datei zu erhalten.
Der übergeordnete Akteur lädt jede Zeile aus der Datei und delegiert dann die Aufgabe, die Wörter in dieser Zeile zu zählen, an einen untergeordneten Akteur. Wenn das Kind fertig ist, sendet es eine Nachricht mit dem Ergebnis an das Elternteil zurück. Der Elternteil empfängt die Nachrichten mit den Wortzählungen (für jede Zeile) und führt einen Zähler für die Gesamtzahl der Wörter in der gesamten Datei, die er dann nach Abschluss an seinen Aufrufer zurückgibt.
(Beachten Sie, dass die unten bereitgestellten Akka-Tutorial-Codebeispiele nur didaktisch gedacht sind und sich daher nicht unbedingt mit allen Randbedingungen, Leistungsoptimierungen usw. befassen. Außerdem ist eine vollständig kompilierbare Version der unten gezeigten Codebeispiele in verfügbar dieses Wesentliche.)
Sehen wir uns zunächst eine Beispielimplementierung der StringCounterActor
-Klasse an:
case class ProcessStringMsg(string: String) case class StringProcessedMsg(words: Integer) class StringCounterActor extends Actor { def receive = { case ProcessStringMsg(string) => { val wordsInLine = string.split(" ").length sender ! StringProcessedMsg(wordsInLine) } case _ => println("Error: message not recognized") } }
Dieser Akteur hat eine sehr einfache Aufgabe: verarbeitet ProcessStringMsg
-Nachrichten (die eine Textzeile enthalten), zählt die Anzahl der Wörter in der angegebenen Zeile und sendet das Ergebnis über eine StringProcessedMsg
Nachricht an den Absender zurück. Beachten Sie, dass wir unsere Klasse implementiert haben, um die !
("tell")-Methode, um die StringProcessedMsg
Nachricht zu senden (dh die Nachricht zu senden und sofort zurückzukehren).

OK, jetzt wenden wir uns der WordCounterActor
zu:
1. case class StartProcessFileMsg() 2. 3. class WordCounterActor(filename: String) extends Actor { 4. 5. private var running = false 6. private var totalLines = 0 7. private var linesProcessed = 0 8. private var result = 0 9. private var fileSender: Option[ActorRef] = None 10. 11. def receive = { 12. case StartProcessFileMsg() => { 13. if (running) { 14. // println just used for example purposes; 15. // Akka logger should be used instead 16. println("Warning: duplicate start message received") 17. } else { 18. running = true 19. fileSender = Some(sender) // save reference to process invoker 20. import scala.io.Source._ 21. fromFile(filename).getLines.foreach { line => 22. context.actorOf(Props[StringCounterActor]) ! ProcessStringMsg(line) 23. totalLines += 1 24. } 25. } 26. } 27. case StringProcessedMsg(words) => { 28. result += words 29. linesProcessed += 1 30. if (linesProcessed == totalLines) { 31. fileSender.map(_ ! result) // provide result to process invoker 32. } 33. } 34. case _ => println("message not recognized!") 35. } 36. }
Hier gehen viele Dinge vor sich, also lassen Sie uns jeden von ihnen genauer untersuchen (beachten Sie, dass die Zeilennummern, auf die in der folgenden Diskussion verwiesen wird, auf dem obigen Codebeispiel basieren) …
Beachten Sie zunächst, dass der Name der zu verarbeitenden Datei an den WordCounterActor
Konstruktor übergeben wird (Zeile 3). Dies zeigt an, dass der Akteur nur verwendet werden soll, um eine einzelne Datei zu verarbeiten. Dies vereinfacht auch die Codierungsaufgabe für den Entwickler, indem die Notwendigkeit vermieden wird, Zustandsvariablen ( running
, totalLines
, linesProcessed
und result
) zurückzusetzen, wenn die Aufgabe erledigt ist, da die Instanz nur einmal verwendet wird (d. h. um eine einzelne Datei zu verarbeiten). und dann verworfen.
Beachten Sie als Nächstes, dass der WordCounterActor
zwei Arten von Nachrichten verarbeitet:
-
StartProcessFileMsg
(Zeile 12)- Wird vom externen Akteur empfangen, der den
WordCounterActor
anfänglich initiiert. - Beim Empfang prüft der
WordCounterActor
zunächst, ob er keine redundante Anfrage erhält. - Wenn die Anfrage redundant ist, generiert
WordCounterActor
eine Warnung und es wird nichts weiter getan (Zeile 16). - Wenn die Anforderung nicht redundant ist:
-
WordCounterActor
speichert eine Referenz auf den Absender in derfileSender
(beachten Sie, dass dies eher eineOption[ActorRef]
als eineOption[Actor]
ist – siehe Zeile 9). DieseActorRef
wird benötigt, um später darauf zuzugreifen und darauf zu reagieren, wenn die letzteStringProcessedMsg
(die von einemStringCounterActor
empfangen wird, wie unten beschrieben). -
WordCounterActor
liest dann die Datei und während jede Zeile in der Datei geladen wird, wird einStringCounterActor
Kind erstellt und ihm wird eine Nachricht mit der zu verarbeitenden Zeile übergeben (Zeile 21-24).
-
- Wird vom externen Akteur empfangen, der den
-
StringProcessedMsg
(Zeile 27)- Wird von einem
StringCounterActor
wenn er die Verarbeitung der ihm zugewiesenen Zeile abschließt. - Beim Empfang inkrementiert der
WordCounterActor
den Zeilenzähler für die Datei und sendet, wenn alle Zeilen in der Datei verarbeitet wurden (dh wenntotalLines
undlinesProcessed
gleich sind), das Endergebnis an den ursprünglichenfileSender
(Zeile 28-31).
- Wird von einem
Beachten Sie noch einmal, dass in Akka der einzige Mechanismus für die Kommunikation zwischen Akteuren die Nachrichtenübermittlung ist. Nachrichten sind das einzige, was Akteure gemeinsam nutzen, und da Akteure möglicherweise gleichzeitig auf dieselben Nachrichten zugreifen können, ist es wichtig, dass sie unveränderlich sind, um Race-Bedingungen und unerwartetes Verhalten zu vermeiden.
Es ist daher üblich, Nachrichten in Form von Fallklassen zu übergeben, da sie standardmäßig unveränderlich sind und sich nahtlos in den Musterabgleich integrieren lassen.
Lassen Sie uns das Beispiel mit dem Codebeispiel zum Ausführen der gesamten App abschließen.
object Sample extends App { import akka.util.Timeout import scala.concurrent.duration._ import akka.pattern.ask import akka.dispatch.ExecutionContexts._ implicit val ec = global override def main(args: Array[String]) { val system = ActorSystem("System") val actor = system.actorOf(Props(new WordCounterActor(args(0)))) implicit val timeout = Timeout(25 seconds) val future = actor ? StartProcessFileMsg() future.map { result => println("Total number of words " + result) system.shutdown } } }
Beachten Sie, wie dieses Mal das ?
Methode wird verwendet, um eine Nachricht zu senden. Auf diese Weise kann der Aufrufer das zurückgegebene Future verwenden, um das Endergebnis auszudrucken, wenn dieses verfügbar ist, und das Programm beenden, indem er das ActorSystem herunterfährt.
Akka-Fehlertoleranz und Supervisor-Strategien
In einem Akteursystem ist jeder Akteur der Supervisor seiner Kinder. Wenn ein Akteur eine Nachricht nicht bearbeitet, suspendiert er sich selbst und alle seine Kinder und sendet eine Nachricht, normalerweise in Form einer Ausnahme, an seinen Supervisor.
In Akka wird die Art und Weise, wie ein Supervisor auf Ausnahmen reagiert und damit umgeht, die von seinen Kindern zu ihm durchsickern, als Supervisor-Strategie bezeichnet. Supervisor-Strategien sind der primäre und unkomplizierte Mechanismus, mit dem Sie das fehlertolerante Verhalten Ihres Systems definieren.
Wenn eine Nachricht, die einen Fehler anzeigt, einen Supervisor erreicht, kann er eine der folgenden Aktionen ausführen:
- Nehmen Sie das Kind (und seine Kinder) wieder auf und behalten Sie seinen internen Zustand bei. Diese Strategie kann angewendet werden, wenn der untergeordnete Zustand nicht durch den Fehler beschädigt wurde und weiterhin ordnungsgemäß funktionieren kann.
- Starten Sie das Kind (und seine Kinder) neu und löschen Sie seinen internen Zustand. Diese Strategie kann im umgekehrten Szenario des gerade beschriebenen verwendet werden. Wenn der untergeordnete Zustand durch den Fehler beschädigt wurde, muss sein Zustand zurückgesetzt werden, bevor er in der Zukunft verwendet werden kann.
- Stoppen Sie das Kind (und seine Kinder) dauerhaft. Diese Strategie kann in Fällen eingesetzt werden, in denen angenommen wird, dass der Fehlerzustand nicht korrigierbar ist, aber den Rest der durchgeführten Operation nicht gefährdet, die in Abwesenheit des ausgefallenen Kindes abgeschlossen werden kann.
- Beenden Sie sich selbst und eskalieren Sie den Fehler. Wird eingesetzt, wenn der Vorgesetzte nicht weiß, wie er mit dem Fehler umgehen soll, und ihn daher an seinen eigenen Vorgesetzten eskaliert.
Darüber hinaus kann ein Akteur entscheiden, die Aktion nur auf die gescheiterten Kinder oder auch auf seine Geschwister anzuwenden. Dafür gibt es zwei vordefinierte Strategien:
-
OneForOneStrategy
: Wendet die angegebene Aktion nur auf das fehlgeschlagene Kind an -
AllForOneStrategy
: Wendet die angegebene Aktion auf alle untergeordneten Elemente an
Hier ist ein einfaches Beispiel mit der OneForOneStrategy
:
import akka.actor.OneForOneStrategy import akka.actor.SupervisorStrategy._ import scala.concurrent.duration._ override val supervisorStrategy = OneForOneStrategy() { case _: ArithmeticException => Resume case _: NullPointerException => Restart case _: IllegalArgumentException => Stop case _: Exception => Escalate }
Wenn keine Strategie angegeben ist, wird die folgende Standardstrategie verwendet:
- Wenn beim Initialisieren des Akteurs ein Fehler aufgetreten ist oder der Akteur getötet wurde, wird der Akteur gestoppt.
- Wenn es eine andere Art von Ausnahme gab, wird der Akteur einfach neu gestartet.
Die von Akka bereitgestellte Implementierung dieser Standardstrategie lautet wie folgt:
final val defaultStrategy: SupervisorStrategy = { def defaultDecider: Decider = { case _: ActorInitializationException ⇒ Stop case _: ActorKilledException ⇒ Stop case _: Exception ⇒ Restart } OneForOneStrategy()(defaultDecider) }
Akka ermöglicht die Implementierung benutzerdefinierter Supervisor-Strategien, aber wie die Akka-Dokumentation warnt, tun Sie dies mit Vorsicht, da falsche Implementierungen zu Problemen wie blockierten Akteursystemen (dh dauerhaft suspendierten Akteuren) führen können.
Standorttransparenz
Die Akka-Architektur unterstützt die Standorttransparenz und ermöglicht es den Akteuren, völlig unabhängig davon zu sein, woher die Nachrichten stammen, die sie erhalten. Der Sender der Nachricht kann sich in derselben JVM wie der Akteur oder in einer separaten JVM befinden (die entweder auf demselben Knoten oder einem anderen Knoten läuft). Akka ermöglicht es, jeden dieser Fälle auf eine Weise zu behandeln, die für den Akteur (und damit den Entwickler) vollständig transparent ist. Der einzige Vorbehalt besteht darin, dass Nachrichten, die über mehrere Knoten gesendet werden, serialisierbar sein müssen.
Akteursysteme sind so konzipiert, dass sie in einer verteilten Umgebung ausgeführt werden können, ohne dass ein spezieller Code erforderlich ist. Akka erfordert lediglich das Vorhandensein einer Konfigurationsdatei ( application.conf
), die die Knoten angibt, an die Nachrichten gesendet werden sollen. Hier ist ein einfaches Beispiel für eine Konfigurationsdatei:
akka { actor { provider = "akka.remote.RemoteActorRefProvider" } remote { transport = "akka.remote.netty.NettyRemoteTransport" netty { hostname = "127.0.0.1" port = 2552 } } }
Ein paar Abschiedstipps…
Wir haben gesehen, wie das Akka-Framework dabei hilft, Parallelität und hohe Leistung zu erreichen. Wie in diesem Tutorial jedoch aufgezeigt, müssen beim Entwerfen und Implementieren Ihres Systems einige Punkte beachtet werden, um die Leistungsfähigkeit von Akka voll auszuschöpfen:
- Jedem Akteur sollte so weit wie möglich die kleinstmögliche Aufgabe zugewiesen werden (wie zuvor besprochen, nach dem Prinzip der Einzelverantwortung).
Akteure sollten Ereignisse (dh Nachrichten verarbeiten) asynchron behandeln und nicht blockieren, da sonst Kontextwechsel stattfinden, die die Leistung beeinträchtigen können. Insbesondere ist es am besten, Blockierungsoperationen (IO usw.) in einem Future durchzuführen, um den Akteur nicht zu blockieren. dh:
case evt => blockingCall() // BAD case evt => Future { blockingCall() // GOOD }
- Stellen Sie sicher, dass alle Ihre Nachrichten unveränderlich sind, da die Akteure, die sie aneinander weitergeben, alle gleichzeitig in ihren eigenen Threads ausgeführt werden. Veränderliche Nachrichten führen wahrscheinlich zu unerwartetem Verhalten.
- Da Nachrichten, die zwischen Knoten gesendet werden, serialisierbar sein müssen, ist es wichtig zu bedenken, dass es umso länger dauert, sie zu serialisieren, zu senden und zu deserialisieren, je größer die Nachrichten sind, was sich negativ auf die Leistung auswirken kann.
Fazit
Akka, geschrieben in Scala, vereinfacht und erleichtert die Entwicklung hochgradig gleichzeitiger, verteilter und fehlertoleranter Anwendungen, wobei ein Großteil der Komplexität vor dem Entwickler verborgen wird. Um Akka vollständig gerecht zu werden, würde viel mehr als dieses einzelne Tutorial erforderlich sein, aber hoffentlich waren diese Einführung und ihre Beispiele ausreichend fesselnd, um Sie dazu zu bringen, mehr lesen zu wollen.
Amazon, VMWare und CSC sind nur einige Beispiele für führende Unternehmen, die Akka aktiv nutzen. Besuchen Sie die offizielle Akka-Website, um mehr zu erfahren und herauszufinden, ob Akka auch für Ihr Projekt die richtige Antwort sein könnte.