Introduktion til Akka Actors i Java

1. Introduktion

Akka er et open source-bibliotek, der hjælper med let at udvikle samtidige og distribuerede applikationer ved hjælp af Java eller Scala ved at udnytte Actor Model.

I denne vejledning Vi præsenterer de grundlæggende funktioner som at definere skuespillere, hvordan de kommunikerer, og hvordan vi kan dræbe dem. I de afsluttende noter vil vi også bemærke nogle bedste fremgangsmåder, når vi arbejder med Akka.

2. Skuespillermodellen

Skuespillermodellen er ikke ny i datalogisk samfund. Det blev først introduceret af Carl Eddie Hewitt i 1973 som en teoretisk model til håndtering af samtidig beregning.

Det begyndte at vise sin praktiske anvendelighed, da softwareindustrien begyndte at indse faldgruber ved implementering af samtidige og distribuerede applikationer.

En aktør repræsenterer en uafhængig beregningsenhed. Nogle vigtige egenskaber er:

  • en aktør indkapsler dens tilstand og en del af applikationslogikken
  • skuespillere interagerer kun gennem asynkrone meddelelser og aldrig gennem direkte metodeopkald
  • hver aktør har en unik adresse og en postkasse, hvor andre aktører kan levere meddelelser
  • skuespilleren behandler alle meddelelserne i postkassen i rækkefølge (standardimplementeringen af ​​postkassen er en FIFO-kø)
  • aktørsystemet er organiseret i et trælignende hierarki
  • en skuespiller kan oprette andre skuespillere, kan sende beskeder til enhver anden skuespiller og stoppe sig selv eller enhver skuespiller er oprettet

2.1. Fordele

Det er svært at udvikle samtidig applikation, fordi vi har brug for synkronisering, låse og delt hukommelse. Ved at bruge Akka-aktører kan vi nemt skrive asynkron kode uden behov for låse og synkronisering.

En af fordelene ved at bruge besked i stedet for metodeopkald er, at afsendertråden blokerer ikke for at vente på en returværdi, når den sender en besked til en anden aktør. Den modtagende aktør svarer med resultatet ved at sende en svarmeddelelse til afsenderen.

En anden stor fordel ved at bruge beskeder er, at vi ikke behøver at bekymre os om synkronisering i et miljø med flere tråde. Dette skyldes, at alle meddelelserne behandles sekventielt.

En anden fordel ved Akka-skuespillermodellen er fejlhåndtering. Ved at organisere skuespillerne i et hierarki kan hver skuespiller underrette sin forælder om fiaskoen, så den kan handle i overensstemmelse hermed. Den overordnede skuespiller kan beslutte at stoppe eller genstarte de underordnede skuespillere.

3. Opsætning

For at drage fordel af Akka-skuespillerne skal vi tilføje følgende afhængighed fra Maven Central:

 com.typesafe.akka akka-actor_2.12 2.5.11 

4. Oprettelse af en skuespiller

Som nævnt er skuespillerne defineret i et hierarkisystem. Alle de aktører, der deler en fælles konfiguration, defineres af en ActorSystem.

For nu definerer vi simpelthen en ActorSystem med standardkonfigurationen og et brugerdefineret navn:

ActorSystem system = ActorSystem.create ("test-system"); 

Selvom vi endnu ikke har oprettet nogen aktører, vil systemet allerede indeholde 3 hovedaktører:

  • rodbeskyttelsesaktøren har adressen “/”, som som navnet angiver repræsenterer roden til skuespillersystemhierarkiet
  • brugerbeskyttende skuespiller, der har adressen “/ bruger”. Dette vil være forælder til alle de skuespillere, vi definerer
  • systemværgens aktør har adressen “/ system”. Dette vil være forælder for alle de aktører, der er defineret internt af Akka-systemet

Enhver Akka-skuespiller udvider Abstrakt Skuespiller abstrakt klasse og implementere createReceive () metode til håndtering af indgående beskeder fra andre aktører:

offentlig klasse MyActor udvider AbstractActor {public Receive createReceive () {return receiveBuilder (). build (); }}

Dette er den mest basale skuespiller, vi kan skabe. Det kan modtage beskeder fra andre aktører og vil kassere dem, fordi der ikke er defineret nogen matchende beskedmønstre i ReceiveBuilder. Vi taler om matchning af meddelelsesmønstre senere i denne artikel.

Nu hvor vi har oprettet vores første skuespiller, skal vi inkludere det i ActorSystem:

ActorRef readingActorRef = system.actorOf (Props.create (MyActor.class), "my-actor");

4.1. Skuespillerkonfiguration

Det Rekvisitter klasse indeholder skuespillerkonfigurationen. Vi kan konfigurere ting som afsenderen, postkassen eller installationskonfigurationen. Denne klasse er uforanderlig og dermed trådsikker, så den kan deles, når du opretter nye aktører.

Det anbefales stærkt og betragtes som en bedste praksis at definere fabriksmetoderne inde i skuespillerobjektet, der håndterer oprettelsen af Rekvisitter objekt.

For at eksemplificere, lad os definere en skuespiller, der vil gøre noget tekstbehandling. Skuespilleren modtager en Snor objekt, hvor det vil behandle:

offentlig klasse ReadingActor udvider AbstractActor {privat strengtekst; offentlige statiske rekvisitter rekvisitter (streng tekst) {returner rekvisitter.create (ReadingActor.class, tekst); } // ...}

Nu, for at oprette en forekomst af denne type skuespiller bruger vi bare rekvisitter() fabriksmetode til at passere Snor argument til konstruktøren:

ActorRef readingActorRef = system.actorOf (ReadingActor.props (TEXT), "readingActor");

Nu hvor vi ved, hvordan man definerer en skuespiller, lad os se, hvordan de kommunikerer inde i skuespillersystemet.

5. Skuespillerbeskeder

For at interagere med hinanden kan skuespillerne sende og modtage beskeder fra enhver anden skuespiller i systemet. Disse meddelelser kan være enhver form for objekt med den betingelse, at den er uforanderlig.

Det er en bedste praksis at definere beskederne inden for skuespillerklassen. Dette hjælper med at skrive kode, der er let at forstå og vide, hvilke beskeder en skuespiller kan håndtere.

5.1. Afsendelse af beskeder

Inde i Akka-skuespilleren sendes systembeskeder ved hjælp af metoder:

  • fortælle()
  • spørge()
  • frem()

Når vi ønsker at sende en besked og ikke forventer et svar, kan vi bruge fortælle() metode. Dette er den mest effektive metode set fra et præstationsperspektiv:

readingActorRef.tell (ny ReadingActor.ReadLines (), ActorRef.noSender ()); 

Den første parameter repræsenterer den besked, vi sender til aktørens adresse readingActorRef.

Den anden parameter specificerer, hvem afsenderen er. Dette er nyttigt, når den skuespiller, der modtager beskeden, skal sende et svar til en anden skuespiller end afsenderen (for eksempel forælderen til den afsendende skuespiller).

Normalt kan vi indstille den anden parameter til nul eller ActorRef.noSender (), fordi vi ikke forventer et svar. Når vi har brug for et svar tilbage fra en skuespiller, kan vi bruge spørge() metode:

CompletableFuture future = ask (wordCounterActorRef, new WordCounterActor.CountWords (line), 1000) .toCompletableFuture ();

Når man beder om svar fra en skuespiller a FuldførelseStage objekt returneres, så behandlingen forbliver ikke-blokering.

En meget vigtig kendsgerning, som vi skal være opmærksomme på, er fejlhåndtering af insider, den aktør, der vil reagere. For at returnere en Fremtid objekt, der indeholder undtagelsen, skal vi sende en Status.Fejl besked til afsenderen.

Dette gøres ikke automatisk, når en skuespiller kaster en undtagelse, mens han behandler en besked og spørge() timeout for opkald, og der vises ingen henvisning til undtagelsen i logfilerne:

@ Override public Receive createReceive () {return receiveBuilder () .match (CountWords.class, r -> {try {int numberOfWords = countWordsFromLine (r.line); getSender (). Tell (numberOfWords, getSelf ());} fange (Undtagelse ex) {getSender (). Fortæl (ny akka.actor.Status.Failure (ex), getSelf ()); kast ex;}}). Build (); }

Vi har også frem() metode, der ligner fortælle(). Forskellen er, at den oprindelige afsender af meddelelsen bevares, når meddelelsen sendes, så den aktør, der videresender meddelelsen, fungerer kun som en mellemled aktør:

printerActorRef.forward (ny PrinterActor.PrintFinalResult (totalNumberOfWords), getContext ());

5.2. Modtagelse af beskeder

Hver aktør implementerer createReceive () metode, som håndterer alle indgående beskeder. Det receiveBuilder () fungerer som en switch-erklæring og forsøger at matche den modtagne besked til den definerede type beskeder:

public Receive createReceive () {return receiveBuilder (). matchEquals ("printit", p -> {System.out.println ("Adressen til denne skuespiller er:" + getSelf ());}). build (); }

Når den modtages, placeres en besked i en FIFO-kø, så meddelelserne håndteres sekventielt.

6. At dræbe en skuespiller

Da vi var færdige med at bruge en skuespiller vi kan stoppe det ved at ringe til hold op() metode fra ActorRefFactory grænseflade:

system.stop (myActorRef);

Vi kan bruge denne metode til at opsige enhver børneskuespiller eller selve skuespilleren. Det er vigtigt at bemærke, at stop sker asynkront, og at den aktuelle meddelelsesbehandling afsluttes inden skuespilleren opsiges. Ingen flere indgående beskeder accepteres i aktørens postkasse.

Ved stopper en forælder skuespiller, Vi sender også et drabssignal til alle de børneaktører der var skabt af det.

Når vi ikke længere har brug for skuespillersystemet, kan vi afslutte det for at frigøre alle ressourcerne og forhindre eventuelle hukommelseslækager:

Fremtidig terminateResponse = system.terminate ();

Dette vil stoppe systemværgens aktører, derfor alle de aktører, der er defineret i dette Akka-system.

Vi kunne også sende en PoisonPill besked til enhver skuespiller, som vi vil dræbe:

myActorRef.tell (PoisonPill.getInstance (), ActorRef.noSender ());

Det PoisonPill besked modtages af skuespilleren som enhver anden besked og sættes i køen. Skuespilleren behandler alle beskederne, indtil den kommer til PoisonPill en. Først da begynder skuespilleren opsigelsesprocessen.

En anden speciel besked, der bruges til at dræbe en skuespiller, er Dræbe besked. I modsætning til Giftpiller, skuespilleren vil kaste et ActorKilledException når du behandler denne meddelelse:

myActorRef.tell (Kill.getInstance (), ActorRef.noSender ());

7. Konklusion

I denne artikel præsenterede vi det grundlæggende i Akka-rammen. Vi viste, hvordan man definerede skuespillere, hvordan de kommunikerer med hinanden, og hvordan man kan afslutte dem.

Vi afslutter med nogle bedste fremgangsmåder, når vi arbejder med Akka:

  • brug fortælle() i stedet for spørge() når ydeevne er et problem
  • når du bruger spørge() vi skal altid håndtere undtagelser ved at sende en Fiasko besked
  • aktører bør ikke dele nogen ændret tilstand
  • en skuespiller bør ikke erklæres inden for en anden skuespiller
  • skuespillere stoppes ikke automatisk når der ikke længere henvises til dem. Vi skal eksplicit ødelægge en skuespiller, når vi ikke længere har brug for det for at forhindre hukommelseslækage
  • beskeder brugt af skuespillere skal altid være uforanderlig

Som altid er kildekoden til artiklen tilgængelig på GitHub.