autoweirdfm / autoweirdfm.github.io

Die alter (a.k.a. legacy) @Autoweird.fm homepage - Kommentare bitte nur noch über die neue Website!
https://autoweirdfm.github.io
5 stars 2 forks source link

Folge 46: Herr Potter und die Aktoren #53

Open holgergp opened 6 years ago

bendisposto commented 6 years ago

Schöne Beschreibung, am Besten finde ich "Das Aktmodell geht auf Gul Abdulnabi Agha zurück" und "Akku Typed" ;-)

holgergp commented 6 years ago

Danke! Fixed!

britter commented 6 years ago

Hehe, da hat die macOS Autokorrektur zugeschlagen 😅

Skyr commented 6 years ago

Aaaalso… ich bin ja auch kein Akka-Guru, aber die offenen Fragen kann ich beantworten, denke ich ;-) Ich nehme mal als Beispiel das akka-http seed (https://github.com/akka/akka-http-scala-seed.g8)

Ein http-Request aus mehreren Teilen beantworten

Ein User wird ja folgendermaßen abgefragt:

get {
  val users: Future[User] = (userRegistryActor ? GetUser(name)).mapTo[User]
  complete(users)
}

complete liefert das Ergebnis des Requests - ein Future, und akka-http kümmert sich unter der Haube um die eigentliche asynchrone Rückgabe.

Nun war eure Frage, was passiert, wenn man nun eine Response aus mehren Futures erzeugt - also z.B. den Userdaten, der Frage nach dem Sinn des Lebens und sonstnochwas ;-) Der "Kunstgriff" ist, mehrere parallele Berechnungen anzustoßen (gibt je ein Future) und diese in ein einzelnes Future zusammenzufassen, welches erfüllt ist, wenn alle parallelen Berechnungen erledigt sind. Dies geht am einfachsten mit einer for-Comprehension. Der Code sieht dann folgendermaßen aus:

get {
  extractExecutionContext { implicit executor =>
    val user: Future[User] =
      (userRegistryActor ? GetUser(user)).mapTo[User]
    val meaningOfLife = Future.successful(42)
    val somethingElse = Future.successful("foobar")
    val allComplete = for {
      a <- users
      b <- somethingElse
      c <- meaningOfLife
    } yield (a,b,c)
    val result = allComplete.map { case (u,_,_) =>
      u // Erzeugt die finale Darstellung. Ok, habe hier nichts gecodet, deshalb einfach nur die Userdaten…
    }
    complete(result)
  }
},

Die extractExecutionContext-Klammer ist erforderlich, da für die for-Comprehension ein Exectuion Context benötigt wird.

Das war's auch schon :-)

Beispiel für Aktoren-Hierarchien

Ihr wart auf der Suche nach einem Beispiel, wo diese Supervision sinnvoll genutzt werden kann. Angeommen, der User Registry Actor würde die Daten tatsächlich aud einer Datenbank holen, und angenommen, es gäbe noch mehr Daten aus der Datenbank. Dann könnte ein darüberliegender Supervisor die Datenbank-Connection verwalten. Explodiert nun einer der DB-Zugriffs-Aktoren wegen Wegbrechen der Datenbank-Verbindung, so kann der darüberliegende Aktor geeignet agieren: Alle anderen Aktoren stoppen (die können ohne DB-Verbindung ja auch nicht funktionieren - da müssen sie ja nicht mühsam selbst in irgendeinen Netzwerk-Timeout o.ä. tappen) und anschießend zentral geeinet reagieren: Verbindungsversuch erst nach ein paar Sekunden, falls erfolglos nach größerer Zeitspanne, etc. Ist die Verbindung wieder da, erzeugt er die DB-Zugriffs-Aktoren neu.

britter commented 6 years ago

@Skyr vielen Dank für deinen ausführlichen Kommentar und sorry, dass ich mich erst jetzt melde. Habe das irgendwie übersehen!

Also klar, du kannst natürlich das ask pattern nutzen um Ergebnisse zusammen führen. Aber ist das wirklich the way to go? Ich sehe hier ein paar Probleme:

  1. eigentlich möchte ich solche Aggregationen nicht in meinem Controller haben. Ich versuche Controller relativ dumm zu machen, sodass sie nur für das ganze Request Handling zuständig sind. Ich habe da am liebsten ein Service Objekt, welche mir dann für einen Request das Ergebnis ermittelt. Der Controller kümmert sich um Input Validierung, JSON Serialisierung, Rückgabe er richtigen Status Code, etc.
  2. Was ist, wenn du auf mehreren Ebenen Subergebnisse sammeln willst? Nutzt du dann überall das ask Pattern? Ich kenne es so, dass man ask eigentlich nur beim Einstieg vom Controller in die Aktoren-Welt nutzt und dann nur noch über tell arbeitet.

Dein Beispiel zur Supervision ist nachvollziehbar aber ich Frage mich, was der Mehrwert der Supervision an dieser Stelle ist. Wie ich im Podcast gesagt habe, habe bin ich noch nie an einen Punkt gekommen, wo ich dieses Konzept vermisst habe. Was bringt es, dass die DB-Zugriffs-Aktoren gestoppt werden? Ohne Supervision kannst du keine Requests mehr bearbeiten, weil deine Datenbank nicht erreichbar ist. Mit Supervision...? Selbes Ergebnis aber dafür die zusätzliche Komplexität entsprechende Supervision zu implementieren. Ob ich in einer Java Anwendung immer wieder mit meinen Repositories gegen eine nicht erreichbare Datenbank renne und dann einen Fehler bekomme oder jemand feststellt, dass die Datenbank nicht erreichbar ist und dann einen Fehler zurück gibt, das macht für mich keinen Unterschied. Aber wahrscheinlich bin ich einfach nicht in der Aktorendenke drin und kann es deshalb nicht nachvollziehen :-)