This repository has been archived by the owner. It is now read-only.

Folge 46: Herr Potter und die Aktoren #53

Open
holgergp opened this Issue Jul 22, 2018 · 5 comments

Comments

Projects
None yet
4 participants
@holgergp
Contributor

holgergp commented Jul 22, 2018

No description provided.

@holgergp holgergp added the Feedback label Jul 22, 2018

@bendisposto

This comment has been minimized.

bendisposto commented Jul 22, 2018

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

@holgergp

This comment has been minimized.

Contributor

holgergp commented Jul 22, 2018

Danke! Fixed!

@britter

This comment has been minimized.

Member

britter commented Jul 23, 2018

Hehe, da hat die macOS Autokorrektur zugeschlagen 😅

@Skyr

This comment has been minimized.

Skyr commented Jul 31, 2018

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

This comment has been minimized.

Member

britter commented Aug 28, 2018

@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 :-)

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.