diff options
| -rw-r--r-- | domain/build.gradle | 11 | ||||
| -rw-r--r-- | domain/src/main/java/presentatie/Bart.java | 70 | ||||
| -rw-r--r-- | domain/src/main/java/presentatie/Marco.java | 101 | ||||
| -rw-r--r-- | presentatie/.goutputstream-E7MM70 | bin | 0 -> 800 bytes | |||
| -rw-r--r-- | presentatie/.goutputstream-QS2S70 | bin | 0 -> 800 bytes | |||
| -rw-r--r-- | presentatie/.goutputstream-WOFL70 | bin | 0 -> 800 bytes | |||
| -rw-r--r-- | presentatie/presentatie.md | 344 | ||||
| -rw-r--r-- | presentatie/thinkingEmoji.png | bin | 0 -> 48043 bytes |
8 files changed, 526 insertions, 0 deletions
diff --git a/domain/build.gradle b/domain/build.gradle index 978d5da..4ee246f 100644 --- a/domain/build.gradle +++ b/domain/build.gradle @@ -50,6 +50,17 @@ application { mainClassName = 'akkamon.domain.iot.IotEntryPoint' } +ext { + BartMarco = "presentatie.Marco" +} + +task BartMarco(type: JavaExec) { + group = "Execution" + description = "Run BartMarco Example" + classpath = sourceSets.main.runtimeClasspath + main = BartMarco +} + run { standardInput = System.in } diff --git a/domain/src/main/java/presentatie/Bart.java b/domain/src/main/java/presentatie/Bart.java new file mode 100644 index 0000000..085b0c9 --- /dev/null +++ b/domain/src/main/java/presentatie/Bart.java @@ -0,0 +1,70 @@ +package presentatie; + +import akka.actor.typed.ActorRef; +import akka.actor.typed.Behavior; +import akka.actor.typed.javadsl.AbstractBehavior; +import akka.actor.typed.javadsl.ActorContext; +import akka.actor.typed.javadsl.Behaviors; +import akka.actor.typed.javadsl.Receive; + +import java.time.Duration; + +public class Bart extends AbstractBehavior<Bart.Bericht> { + + public interface Bericht {} + + public static class reviewMijnCodeAlsjeblieft implements Bericht { + public ActorRef<isKlaarMetCodeReview> replyTo; + public reviewMijnCodeAlsjeblieft(ActorRef<isKlaarMetCodeReview> replyTo) { + this.replyTo = replyTo; + } + } + + public static class isKlaarMetCodeReview implements Bericht, Marco.Bericht { + public String string; + public isKlaarMetCodeReview(String string) { + this.string = string; + } + } + + public static Behavior<Bart.Bericht> create() { + return Behaviors.setup(context -> new Bart(context)); + } + + public Bart(ActorContext<Bart.Bericht> context) { + super(context); + } + + @Override + public Receive<Bart.Bericht> createReceive() { + return newReceiveBuilder() + .onMessage(Bart.reviewMijnCodeAlsjeblieft.class, this::reviewMarcosCode) + .build(); + } + + private Behavior<Bericht> bezigMetCodeReview() { + return newReceiveBuilder() + .onMessage(Bart.reviewMijnCodeAlsjeblieft.class, this::benAlBezig) + .build(); + } + + private Behavior<Bericht> reviewMarcosCode(reviewMijnCodeAlsjeblieft codeReviewVraag) { + getContext().getLog().info("Bart ontvangt vraag van Marco en begint met code review, en stuurt een reactie als hij klaar is."); + Duration seconds = Duration.ofSeconds(7); + getContext().getSystem().scheduler().scheduleOnce(seconds, new Runnable() { + @Override + public void run() { + codeReviewVraag.replyTo.tell(new isKlaarMetCodeReview("Ben klaar met de code review!")); + } + }, getContext().getExecutionContext()); + return bezigMetCodeReview(); + } + + private Behavior<Bericht> benAlBezig(reviewMijnCodeAlsjeblieft codeReviewVraag) { + getContext().getLog().info("Bart ontvangt tweede keer de vraag van Marco en reageert nu gelijk."); + codeReviewVraag.replyTo.tell(new isKlaarMetCodeReview("Ben al bezig met de code review, Life is bad.")); + return this; + } + + +} diff --git a/domain/src/main/java/presentatie/Marco.java b/domain/src/main/java/presentatie/Marco.java new file mode 100644 index 0000000..8da2f4f --- /dev/null +++ b/domain/src/main/java/presentatie/Marco.java @@ -0,0 +1,101 @@ +package presentatie; + +import akka.actor.typed.ActorRef; +import akka.actor.typed.ActorSystem; +import akka.actor.typed.Behavior; +import akka.actor.typed.javadsl.AbstractBehavior; +import akka.actor.typed.javadsl.ActorContext; +import akka.actor.typed.javadsl.Behaviors; +import akka.actor.typed.javadsl.Receive; + +import java.time.Duration; +import java.util.concurrent.TimeUnit; + +public class Marco extends AbstractBehavior<Marco.Bericht> { + + public interface Bericht {} + + private static class MarcoBegrijptReactieZo implements Bericht { + public String string; + + public MarcoBegrijptReactieZo(String s) { + this.string = s; + } + } + + public static Behavior<Marco.Bericht> create() { + return Behaviors.setup(context -> new Marco(context)); + } + + private boolean benalbezigOntvangen = false; + + public Marco(ActorContext<Bericht> context) { + super(context); + + ActorRef<Bart.Bericht> bart = getContext().spawn(Bart.create(), "bartsNaam"); + + final Duration timeout = Duration.ofSeconds(10); + + getContext().getLog().info("Hey Bart, ik heb net Aurorus gerefactord kan je mijn code reviewen?"); + context.ask( + Bart.isKlaarMetCodeReview.class, + bart, + timeout, + (ActorRef<Bart.isKlaarMetCodeReview> ref) -> new Bart.reviewMijnCodeAlsjeblieft(ref), + (reactie, throwable) -> { + if (reactie != null) { + return new MarcoBegrijptReactieZo(reactie.string); + } else { + return new MarcoBegrijptReactieZo("Code Review failed?!"); + } + } + ); + + try { + TimeUnit.SECONDS.sleep(3); + } catch (InterruptedException e) { + e.printStackTrace(); + } + + context.ask( + Bart.isKlaarMetCodeReview.class, + bart, + timeout, + (ActorRef<Bart.isKlaarMetCodeReview> ref) -> new Bart.reviewMijnCodeAlsjeblieft(ref), + (reactie, throwable) -> { + if (reactie != null) { + return new MarcoBegrijptReactieZo(reactie.string); + } else { + return new MarcoBegrijptReactieZo("Code Review failed?!"); + } + } + ); + } + + @Override + public Receive<Bericht> createReceive() { + return newReceiveBuilder() + .onMessage(MarcoBegrijptReactieZo.class, this::ontvangReactie) + .build(); + } + + private Behavior<Bericht> ontvangReactie(MarcoBegrijptReactieZo reactie) { + getContext().getLog().info("Marco ontvangt reactie van Bart: " + reactie.string); + return stopAlsDemoVoorbijIs(); + } + + private Behavior<Bericht> stopAlsDemoVoorbijIs() { + if (benalbezigOntvangen) { + return Behaviors.stopped(); + } else { + this.benalbezigOntvangen = true; + return this; + } + } + + public static void main(String[] args) { + System.out.println("Hello all!"); + ActorRef<Bericht> system = ActorSystem.create(Marco.create(), "presentatie-voorbeeld"); + } + +} diff --git a/presentatie/.goutputstream-E7MM70 b/presentatie/.goutputstream-E7MM70 Binary files differnew file mode 100644 index 0000000..acf049c --- /dev/null +++ b/presentatie/.goutputstream-E7MM70 diff --git a/presentatie/.goutputstream-QS2S70 b/presentatie/.goutputstream-QS2S70 Binary files differnew file mode 100644 index 0000000..acf049c --- /dev/null +++ b/presentatie/.goutputstream-QS2S70 diff --git a/presentatie/.goutputstream-WOFL70 b/presentatie/.goutputstream-WOFL70 Binary files differnew file mode 100644 index 0000000..662feb7 --- /dev/null +++ b/presentatie/.goutputstream-WOFL70 diff --git a/presentatie/presentatie.md b/presentatie/presentatie.md new file mode 100644 index 0000000..f641a5c --- /dev/null +++ b/presentatie/presentatie.md @@ -0,0 +1,344 @@ +--- +title: Akkamon +author: Mike Vink, Sogyo +extensions: + - terminal + - image_ueberzug +--- + +| | +| ----------- | +| | +| | +| | +| | +| | +| | +| | +| | +| | +| Akkamon: een mmo maken met akka en phaser3 | +| | +| | +| | +| | +| | + +--- + + + +Hoe maak je eigenlijk een mmo? Wat is eigenlijk een mmo? + +--- + + + +Hoe maak je eigenlijk een mmo? + +* Veel spelers tegelijk laten bewegen in de wereld +* Spelers een interactie met elkaar laten aangaan +* Een spel + +--- + +# What is an actor? A computation unit in and of itself + +> " The actor is the fundamental unit of computation. It has to embody three things: +* processing - because you gotta get something done +* storage - because you have to be able to remember things +* and, communications" + + +- Carl Hewitt, creator of the actor model, very smart old guy + +from very cool and funny talk: [The Actor Model (everything you wanted to know, but were afraid to ask)](https://channel9.msdn.com/Shows/Going+Deep/Hewitt-Meijer-and-Szyperski-The-Actor-Model-everything-you-wanted-to-know-but-were-afraid-to-ask) + +--- + +# Actors don't come alone, they come in a system + +> "One ant is no ant." + +* In het systeem is alles een actor + +* Elke actor heeft een eigen address in het systeem + +* Een actor kan een bericht sturen naar een actor met een address in het systeem + +``` +--> Een actor heeft een mailbox waar hij berichten kan ontvangen +``` + + +```actor + ┌──────┐ bericht ┌──────┐ + │Actor1│ ------> ------>│Actor2│ + └──────┘ └──────┘ +``` + +--- + +# Hoe werkt het precies? Is de mailbox ook een actor? Nee + + +Wat kan een actor precies wanneer een bericht ontvangen wordt??: + +I. Hij kan een nieuwe actor maken/spawnen + + +``` + bericht ┌──────┐ ┌────────┐ +-------->│Actor1│ .spawn ---> │newActor│ + └──────┘ └────────┘ +``` + +II. Hij kan een nieuw berict sturen naar een adres wat hij kent (in een HashMap bijv.) + +``` + bericht ┌──────┐ bericht ┌─────────────┐ +-------->│Actor1│ .tell ------> │bekendeActeur│ + └──────┘ └─────────────┘ +``` + +III. Hij kan opgeven wat hij met volgende berichten gaat doen (denk gedrag van een object) + +``` + bericht ┌──────────┐ ┌─────────┐ +--------> │blijeActor│ .behaviorBoos ---> │bozeActor│ <- (hij reageert nu boos op berichten) + └──────────┘ └─────────┘ +``` + +--- + +# Conceptueel gezien wordt 1 bericht per keer afgehandelt + +* Berichten versturen kan behandelt worden als *"Fire and Forget"* + * de verzender kan gelijk iets anders doen + * de ontvanger handelt het bericht pas af wanneer hij er klaar voor is + +``` + + + bericht4 + bericht3 bericht4 + bericht2 bericht3 + bericht1 bericht2 + ┌───────┐ ┌───────┐ + bericht4 │mailbox│┌──────────┐ │mailbox│┌──────────┐ +--------> └───────┘ Actor │ .handelBericht1Af ---> └───────┘ Actor │ ---> ... + └───────────────────┘ └───────────────────┘ +``` + +--- + +# Wat is het voordeel van het Actor Systeem? No channel, no overhead! Fast parallelism + +* Geen intermediary stap tussen processen! Data kan snel tussen verschillende processen +uitgewisseld worden. + + * Geen zorgen over channel overhead en logica + * Geen two fase commit bijvoorbeeld (GET, PUT) + + +* Abstractie laag dat parallelisme developpen "super" makkelijk maakt + + - geen lowlevel geneuzel met locks, queues, en andere implementatie details + * makkelijke optimalisatie voor distributed computing + +--- + +# Sidenote: Programs, processes, and threads + +[Threads vs. processes](https://www.backblaze.com/blog/whats-the-diff-programs-processes-and-threads/) + +--- + +# And there is more! Dingen die ik niet veel gebruikt heb! + +* Futures/ berichten naar de toekomst versturen. Denk aan javascript promises: + +```js +let theFuture = () => { new Promise(resolve => {setTimeout(resolve, 1000)} ) } +await theFuture(); +``` + +* _Many to many_ relaties tussen actors en hun adressen (1 actor met veel adressen niet gebruikt) + +* Berichten ensurance of arrival () + +* Non- determinisme (Uitkomst van systeem hangt af van factoren buiten het systeem) + +* Synchronisation en mutability van data (built-in alleen, de 1 message at a time regel) + +* Arbiters indeterminisme + +* Van adress naar actor gaan (implementatie detail) + +> " This is the end. Nobody could ever conceive of a model of computation beyond that of the Turing machine. " + +--- + +# Java Akka typed actor programming language + +## Voorbeeld: Een actor (systeem) maken met het ask patroon + +* Stel we hebben twee Actors: Marco en Bart + +```java +class Marco { +} + +class Bart { +} +``` + +* Allebei kunnen ze een type bericht ontvangen + +```java +class Marco extends AbstractBehavior<Marco.Bericht> { + public interface Bericht {} // bericht type wat Marco kan ontvangen + + + public static Behavior<Marco.Bericht> create() { // Maak een marco actor referentie + return Behaviors.setup(context -> new Marco(context)); + } + + public Marco(ActorContext<Bericht> context) {super(context)} // Maak een Marco object + + @Override + public Receive<Bericht> createReceive() { // Wat doe ik als ik een bericht ontvang? + return newReceiveBuilder() + .build(); + } +} + +class Bart // zelfde verhaal +``` + +--- + +# Voorbeeld: Een actor (systeem) maken met het ask patroon + +* In ons geval begint het systeem wanneer Marco klaar is met een grote Aurorus refactor + +```java +public static main(String[] args) { + ActorRef<Marco.Bericht> marco = ActorSystem.create(Marco.create(), "aurorus-refactor-systeem"); +} +``` + +* Wanneer het systeem start maakt Marco een Bart actor (niet super realistisch) en vraagt hem om een Code Review te doen + +```java +[..] +public Marco() { + ActorRef<Bart.Bericht> bart = getContext().spawn(Bart.create(), "bartsNaam"); + final Duration timeout = Duration.ofSeconds(10); + // ask en tell zijn belangrijk + context.ask( + Bart.isKlaarMetCodeReview.class, // Verwacht deze reactie + bart, // vraag het aan bart + timeout, // hoe lang heeft bart om te reageren? + (ActorRef<Bart.isKlaarMetCodeReview> ref) -> new Bart.reviewMijnCodeAlsjeblieft(ref), // welke vraag stuur ik + (reactie, throwable) -> { + if (reactie != null) { + return new MarcoBegrijptReactieZo(reactie.string); + } else { + return new MarcoBegrijptReactieZo("Er ging iets fout!"); + } + } + ); +} +[..] +``` + +--- + +# Voorbeeld: een actor dat van gedrag verandert na een bericht + +* Als je bart vraagt om een code review terwijl hij al bezig is reageert hij anders: + +``` +bash -c "gradle :domain:BartMarco" +``` + +--- + +# Wat gebeurde er net? + +* Toen Bart de eerste code review vraag ontving van Marco veranderde hij zijn gedrag naar *"ben al bezig"*. + +```java +class Bart ... +[...] + @Override + public Receive<Bart.Bericht> createReceive() { + return newReceiveBuilder() + .onMessage(Bart.reviewMijnCodeAlsjeblieft.class, this::reviewMarcosCode) + .build(); + } + + private Behavior<Bericht> bezigMetCodeReview() { + return newReceiveBuilder() + .onMessage(Bart.reviewMijnCodeAlsjeblieft.class, this::benAlBezig) + .build(); + } + + private Behavior<Bericht> reviewMarcosCode(reviewMijnCodeAlsjeblieft codeReviewVraag) { + getContext().getLog().info("Bart ontvangt vraag van Marco en begint met code review, en stuurt een reactie als hij klaar is."); + stuurEenReactieWanneerIkKlaarBen(); + return bezigMetCodeReview(); // <------------ Gedrag wordt verandert naar "ben al bezig"! + } + + private Behavior<Bericht> benAlBezig(reviewMijnCodeAlsjeblieft codeReviewVraag) { + getContext().getLog().info("Bart ontvangt tweede keer de vraag van Marco en reageert nu gelijk."); + codeReviewVraag.replyTo.tell(new isKlaarMetCodeReview("Ben al bezig met de code review, Life is bad.")); + return this; + } +[...] +``` + +--- + +# Samenvatting + +* Ons systeem + +``` + + ┌─────┐ ReviewMijnCodeAlsjeblieft ┌─────────────────────┐ + │Marco│ ---> ---> │Bart (Niet bezig) │ + └─────┘ └─────────────────────┘ + | \ + | \ + | ┌─────────────────────┐ + \ │Bart (Ben al bezig) │ + \ └─────────────────────┘ + \ / + \ / wanneerIkKlaarBen + <---Ben klaar met de review! <--- + + + ┌─────┐ ReviewMijnCodeAlsjeblieft ┌─────────────────────┐ + │Marco│ ---> ---> │Bart (Ben al bezig) │ + └─────┘ └─────────────────────┘ + \ / + \ / gelijk + <---Ben al bezig, Life is Bad <--- + +``` + +* Actor berichten zijn data objecten welke een interface implementeren dat aangeeft naar welke actor het gestuurd kan worden + +* Actor berichten kunnen met .tell() en .ask() worden gecommuniceerd + +* Actor gedrag kan veranderen door een .onMessage functie een andere behavior te laten returnen + +> Bart gaat niet terug naar de niet bezig staat in ons voorbeeld systeem, hoe zouden we Barts gedrag kunnen veranderen? + +--- + +# Het actor systeem van akkamon: spelers laten bewegen in de wereld + + diff --git a/presentatie/thinkingEmoji.png b/presentatie/thinkingEmoji.png Binary files differnew file mode 100644 index 0000000..5f482b5 --- /dev/null +++ b/presentatie/thinkingEmoji.png |
