Introduksjon til Akka Actors i Java

1. Introduksjon

Akka er et open source-bibliotek som hjelper deg med å enkelt utvikle samtidige og distribuerte applikasjoner ved hjelp av Java eller Scala ved å utnytte skuespillermodellen.

I denne veiledningen, Vi presenterer de grunnleggende funksjonene som å definere skuespillere, hvordan de kommuniserer og hvordan vi kan drepe dem. I de siste merknadene vil vi også legge merke til noen gode fremgangsmåter når du arbeider med Akka.

2. Skuespillermodellen

Skuespillermodellen er ikke ny i informatikkfellesskapet. Den ble først introdusert av Carl Eddie Hewitt i 1973, som en teoretisk modell for håndtering av samtidig beregning.

Det begynte å vise sin praktiske anvendelighet da programvareindustrien begynte å innse fallgruvene ved implementering av samtidige og distribuerte applikasjoner.

En aktør representerer en uavhengig beregningsenhet. Noen viktige egenskaper er:

  • en aktør innkapsler tilstanden og en del av applikasjonslogikken
  • skuespillere samhandler bare gjennom asynkrone meldinger og aldri gjennom direkte metodesamtaler
  • hver aktør har en unik adresse og en postkasse der andre aktører kan levere meldinger
  • skuespilleren vil behandle alle meldingene i postkassen i sekvensiell rekkefølge (standard implementering av postkassen er en FIFO-kø)
  • skuespillersystemet er organisert i et trelignende hierarki
  • en skuespiller kan opprette andre skuespillere, kan sende meldinger til andre skuespillere og stoppe seg selv eller en hvilken som helst skuespiller er opprettet

2.1. Fordeler

Å utvikle samtidig applikasjon er vanskelig fordi vi trenger å håndtere synkronisering, låser og delt minne. Ved å bruke Akka-aktører kan vi enkelt skrive asynkron kode uten behov for låser og synkronisering.

En av fordelene med å bruke melding i stedet for metodesamtaler er at avsendertråden vil ikke blokkere for å vente på en returverdi når den sender en melding til en annen aktør. Den mottakende skuespilleren vil svare med resultatet ved å sende en svarmelding til avsenderen.

En annen stor fordel med å bruke meldinger er at vi ikke trenger å bekymre oss for synkronisering i et miljø med flere tråder. Dette er på grunn av det faktum at alle meldingene behandles sekvensielt.

En annen fordel med Akka-skuespillermodellen er feilhåndtering. Ved å organisere skuespillerne i et hierarki, kan hver skuespiller varsle foreldrene om feilen, slik at den kan handle deretter. Den overordnede skuespilleren kan bestemme seg for å stoppe eller starte barneaktørene på nytt.

3. Oppsett

For å dra nytte av Akka-skuespillerne må vi legge til følgende avhengighet fra Maven Central:

 com.typesafe.akka akka-actor_2.12 2.5.11 

4. Opprette en skuespiller

Som nevnt er skuespillerne definert i et hierarkisystem. Alle aktørene som deler en felles konfigurasjon vil bli definert av en ActorSystem.

For nå vil vi ganske enkelt definere en ActorSystem med standardkonfigurasjonen og et tilpasset navn:

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

Selv om vi ikke har opprettet noen aktører ennå, vil systemet allerede inneholde 3 hovedaktører:

  • root guardian actor som har adressen “/” som som navnet angir representerer roten til aktørens systemhierarki
  • brukeren verge skuespiller som har adressen “/ bruker”. Dette vil være foreldre til alle skuespillere vi definerer
  • systemets verge aktør har adressen “/ system”. Dette vil være foreldre for alle aktørene som er definert internt av Akka-systemet

Enhver Akka-skuespiller vil utvide AbstractActor abstrakt klasse og implementere createReceive () metode for håndtering av innkommende meldinger fra andre aktører:

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

Dette er den mest grunnleggende skuespilleren vi kan skape. Den kan motta meldinger fra andre aktører og vil forkaste dem fordi ingen samsvarende meldingsmønstre er definert i ReceiveBuilder. Vi snakker om meldingstilpasning senere i denne artikkelen.

Nå som vi har opprettet vår første skuespiller, bør vi inkludere den i ActorSystem:

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

4.1. Skuespillerkonfigurasjon

De Rekvisitter klasse inneholder skuespillerkonfigurasjonen. Vi kan konfigurere ting som senderen, postkassen eller distribusjonskonfigurasjonen. Denne klassen er uforanderlig, og dermed trådsikker, så den kan deles når du lager nye skuespillere.

Det er sterkt anbefalt og betraktet som en best-praksis å definere fabrikkmetodene inne i skuespillerobjektet som vil håndtere opprettelsen av Rekvisitter gjenstand.

For å eksemplifisere, la oss definere en skuespiller som vil gjøre litt tekstbehandling. Skuespilleren vil motta en String objekt som den vil behandle på:

offentlig klasse ReadingActor utvider AbstractActor {privat strengtekst; offentlige statiske rekvisitter rekvisitter (strengtekst) {return Props.create (ReadingActor.class, text); } // ...}

Nå, for å lage en forekomst av denne typen skuespillere bruker vi bare Rekvisitter() fabrikken metode for å passere String argument til konstruktøren:

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

Nå som vi vet hvordan vi skal definere en skuespiller, la oss se hvordan de kommuniserer inne i skuespillersystemet.

5. Skuespillermeldinger

For å samhandle med hverandre, kan skuespillerne sende og motta meldinger fra andre aktører i systemet. Disse meldinger kan være en hvilken som helst type gjenstand med den forutsetning at den er uforanderlig.

Det er en best praksis å definere meldingene i skuespillerklassen. Dette hjelper til med å skrive kode som er lett å forstå og vite hvilke meldinger en skuespiller kan håndtere.

5.1. Sende meldinger

Inne i Akka skuespilleren blir systemmeldinger sendt ved hjelp av metoder:

  • fortelle()
  • spørre()
  • framover()

Når vi ønsker å sende en melding og ikke forventer svar, kan vi bruke fortelle() metode. Dette er den mest effektive metoden fra et ytelsesperspektiv:

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

Den første parameteren representerer meldingen vi sender til aktørens adresse readingActorRef.

Den andre parameteren spesifiserer hvem avsenderen er. Dette er nyttig når skuespilleren som mottar meldingen må sende et svar til en annen skuespiller enn avsenderen (for eksempel foreldrene til den sendende skuespilleren).

Vanligvis kan vi sette den andre parameteren til null eller ActorRef.noSender (), fordi vi ikke forventer svar. Når vi trenger svar tilbake fra en skuespiller, kan vi bruke spørre() metode:

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

Når du ber om svar fra en skuespiller a CompletionStage objektet returneres, så behandlingen forblir ikke-blokkerende.

Et veldig viktig faktum som vi må ta hensyn til, er feilhåndtering av insideren skuespilleren som vil svare. Å returnere en Framtid objekt som inneholder unntaket, må vi sende et Status.Feil melding til avsenderen.

Dette gjøres ikke automatisk når en skuespiller kaster et unntak mens han behandler en melding og spørre() tidsavbrudd for anrop, og ingen referanser til unntaket blir sett i loggene:

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

Vi har også framover() metode som ligner på fortelle(). Forskjellen er at den opprinnelige avsenderen av meldingen blir oppbevart når du sender meldingen, så skuespilleren som videresender meldingen fungerer kun som en mellomledd aktør:

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

5.2. Motta meldinger

Hver aktør vil implementere createReceive () metode, som håndterer alle innkommende meldinger. De motta bygging () fungerer som en bryteruttalelse, og prøver å matche den mottatte meldingen til den definerte typen meldinger:

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

Når den mottas, blir en melding satt i en FIFO-kø, slik at meldingene håndteres sekvensielt.

6. Drepe en skuespiller

Da vi var ferdig med å bruke en skuespiller vi kan stoppe det ved å ringe Stoppe() metode fra ActorRefFactory grensesnitt:

system.stop (myActorRef);

Vi kan bruke denne metoden for å avslutte enhver barneskuespiller eller selve skuespilleren. Det er viktig å merke seg at stopp er gjort asynkront, og at den nåværende meldingsbehandlingen er ferdig før skuespilleren avsluttes. Ingen flere innkommende meldinger aksepteres i skuespillerens postkasse.

Av stopper en foreldreskuespiller, Vi sender også et drapsignal til alle barneaktørene som ble skapt av det.

Når vi ikke trenger aktørsystemet lenger, kan vi avslutte det for å frigjøre alle ressursene og forhindre minnelekkasjer:

Future terminateResponse = system.terminate ();

Dette vil stoppe systemets vergeaktører, derav alle aktørene som er definert i dette Akka-systemet.

Vi kan også sende en PoisonPill beskjed til enhver skuespiller som vi ønsker å drepe:

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

De PoisonPill meldingen vil bli mottatt av skuespilleren som alle andre meldinger og settes i køen. Skuespilleren vil behandle alle meldingene til den kommer til PoisonPill en. Først da vil skuespilleren begynne avslutningsprosessen.

En annen spesiell melding som brukes til å drepe en skuespiller er Drepe beskjed. i motsetning til PoisonPill, skuespilleren vil kaste en ActorKilledException når du behandler denne meldingen:

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

7. Konklusjon

I denne artikkelen presenterte vi det grunnleggende i Akka-rammeverket. Vi viste hvordan vi skal definere skuespillere, hvordan de kommuniserer med hverandre og hvordan de kan avsluttes.

Vi vil avslutte med noen gode fremgangsmåter når du jobber med Akka:

  • bruk fortelle() i stedet for spørre() når ytelse er en bekymring
  • når du bruker spørre() vi bør alltid håndtere unntak ved å sende en Feil beskjed
  • skuespillere bør ikke dele noen foranderlig tilstand
  • en skuespiller skal ikke erklæres i en annen skuespiller
  • skuespillere stoppes ikke automatisk når det ikke lenger refereres til dem. Vi må eksplisitt ødelegge en skuespiller når vi ikke trenger det lenger for å forhindre minnelekkasjer
  • meldinger brukt av skuespillere skal alltid være uforanderlig

Som alltid er kildekoden for artikkelen tilgjengelig på GitHub.


$config[zx-auto] not found$config[zx-overlay] not found