Bevezetés az Akka Actors-ba Java-ban

1. Bemutatkozás

Az Akka egy nyílt forráskódú könyvtár, amely segíti az egyidejű és elosztott alkalmazások egyszerű fejlesztését Java vagy Scala használatával az Actor Model kihasználásával.

Ebben az oktatóanyagban bemutatjuk azokat az alapvető jellemzőket, mint a színészek meghatározása, a kommunikáció módja és hogyan tudjuk megölni őket. Az utolsó megjegyzésekben néhány bevált gyakorlatot is megemlítünk az Akkával való együttműködés során.

2. A színész modell

A színész modell nem új keletű a számítástechnikai közösség számára. Először Carl Eddie Hewitt vezette be 1973-ban, mint elméleti modell az egyidejű számítás kezelésére.

Akkor kezdett megmutatkozni gyakorlati alkalmazhatóságán, amikor a szoftveripar elkezdte felismerni az egyidejű és elosztott alkalmazások megvalósításának buktatóit.

A színész egy független számítási egységet képvisel. Néhány fontos jellemző:

  • egy szereplő összefoglalja állapotát és az alkalmazáslogika egy részét
  • a szereplők csak aszinkron üzeneteken keresztül kommunikálnak, és soha nem közvetlen módszeres hívásokon keresztül
  • minden szereplőnek egyedi címe és postaládája van, amelyben a többi szereplő üzeneteket küldhet
  • a színész a postaládában lévő összes üzenetet sorrendben dolgozza fel (a postafiók alapértelmezett megvalósítása FIFO-sor)
  • a színészrendszer faszerű hierarchiába szerveződik
  • egy színész létrehozhat más színészeket, üzeneteket küldhet bármely más színésznek és megállíthatja önmagát, vagy bármelyik szereplő létrejött

2.1. Előnyök

Az egyidejű alkalmazás fejlesztése nehéz, mert szinkronizálással, zárakkal és megosztott memóriával kell foglalkoznunk. Az Akka színészek használatával könnyen írhatunk aszinkron kódot zárak és szinkronizálás nélkül.

A módszerhívások helyett az üzenet használatának egyik előnye az a küldő szál nem blokkolja a visszatérési érték megvárását, amikor üzenetet küld egy másik szereplőnek. A fogadó szereplő válaszol az eredménnyel, és válaszüzenetet küld a feladónak.

Az üzenetek használatának másik nagy előnye, hogy nem kell aggódnunk a szálak szinkronizálásáért egy több szálat tartalmazó környezetben. Ennek oka az a tény, hogy az összes üzenetet egymás után dolgozzuk fel.

Az Akka színész modell másik előnye a hibakezelés. A szereplők hierarchiába rendezésével minden szereplő értesítheti szüleit a kudarcról, így ennek megfelelően járhat el. A szülő színész dönthet úgy, hogy leállítja vagy újraindítja a gyermek színészeket.

3. Beállítás

Az Akka színészek előnyeinek kihasználásához hozzá kell adnunk a következő függőséget a Maven Centraltól:

 com.typesafe.akka akka-színész_2.12 2.5.11 

4. Színész létrehozása

Mint említettük, a szereplőket hierarchia rendszerben határozzák meg. Az összes szereplőt, akinek közös konfigurációja van, egy ActorSystem.

Egyelőre egyszerűen definiálunk egy ActorSystem alapértelmezett konfigurációval és egyéni névvel:

ActorSystem system = ActorSystem.create ("tesztrendszer"); 

Annak ellenére, hogy még nem hoztunk létre színészeket, a rendszer már 3 fő szereplőt fog tartalmazni:

  • a „/” címmel rendelkező gyámvédő színész, amely névként a színész rendszer hierarchiájának gyökerét képviseli
  • a „/ felhasználó” címet viselő felhasználói gyám színész. Ez lesz az összes általunk meghatározott színész szülője
  • a „/ rendszer” címet viselő rendszergazda szereplő. Ez lesz a szülő minden olyan szereplő számára, amelyet az Akka rendszer belsőleg meghatároz

Bármely Akka színész meghosszabbítja a AbstractActor absztrakt osztály és valósítsa meg a createReceive () módszer a többi szereplőtől érkező üzenetek kezelésére:

public class A MyActor kiterjeszti az AbstractActor {public Receive createReceive () {return ReceiveBuilder (). build (); }}

Ez a legalapvetőbb színész, akit létrehozhatunk. Üzeneteket fogadhat más szereplőktől, és eldobja őket, mert a ReceiveBuilder. Az üzenetminták egyeztetéséről a cikk későbbi szakaszában fogunk beszélni.

Most, hogy megalkottuk az első színészt, be kell vonnunk a ActorSystem:

ActorRef readingActorRef = system.actorOf (Props.create (MyActor.class), "én-színész");

4.1. Színész beállítása

A Kellékek osztály tartalmazza a színész konfigurációját. Konfigurálhatunk például diszpécsert, postaládát vagy telepítési konfigurációt. Ez az osztály változhatatlan, így szálbiztos, így új szereplők létrehozásakor megosztható.

Nagyon ajánlott és bevált gyakorlatnak tekinthető a gyári módszerek meghatározása a színészobjektumon belül, amely az objektum létrehozását kezeli Kellékek tárgy.

Példaként említsünk meg egy szereplőt, aki elvégzi a szövegfeldolgozást. A színész a Húr objektum, amelyen elvégzi a feldolgozást:

public class ReadingActor kiterjeszti az AbstractActor {private String text; nyilvános statikus kellékek kellékei (karakterlánc szöveg) {return kellékek létrehozása (ReadingActor.osztály, szöveg); } // ...}

Most, hogy létrehozzunk egy ilyen típusú színészt, csak a kellékek () gyári módszer a Húr argumentum a konstruktorral:

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

Most, hogy tudjuk, hogyan kell meghatározni egy színészt, nézzük meg, hogyan kommunikálnak a színészrendszeren belül.

5. Színész Üzenetek

A szereplők, hogy kölcsönhatásba léphessenek egymással, üzeneteket küldhetnek és fogadhatnak a rendszer bármely más szereplőjétől. Ezek az üzenetek bármilyen típusú objektumok lehetnek, azzal a feltétellel, hogy megváltoztathatatlanok.

A legjobb gyakorlat, ha meghatározza az üzeneteket a színészosztályon belül. Ez segít olyan kód megírásában, amely könnyen érthető és tudja, milyen üzeneteket képes kezelni a színész.

5.1. Üzenetek küldése

Az Akka színész rendszeren belül az üzeneteket a következő módszerekkel küldik:

  • Mondd()
  • kérdez()
  • előre()

Ha üzenetet akarunk küldeni, és nem várunk választ, használhatjuk a Mondd() módszer. Teljesítmény szempontjából ez a leghatékonyabb módszer:

readingActorRef.tell (új ReadingActor.ReadLines (), ActorRef.noSender ()); 

Az első paraméter azt az üzenetet jelenti, amelyet a szereplő címére küldünk readingActorRef.

A második paraméter meghatározza, hogy ki a feladó. Ez akkor hasznos, ha az üzenetet fogadó szereplőnek választ kell küldenie a feladótól eltérő szereplőnek (például a küldő színész szülőjének).

Általában a második paramétert beállíthatjuk nulla vagy ActorRef.noSender (), mert nem várunk választ. Amikor egy színész válaszára van szükségünk, használhatjuk a kérdez() módszer:

CompletableFuture future = ask (wordCounterActorRef, új WordCounterActor.CountWords (sor), 1000) .toCompletableFuture ();

Amikor választ kér egy színésztől a CompletionStage Az objektum visszaküldik, így a feldolgozás nem blokkoló marad.

Egy nagyon fontos tény, amelyre figyelnünk kell, a hibakezelés a bennfentes szereplőben, amely válaszolni fog. Visszatérés a Jövő objektum, amely tartalmazza a kivételt, el kell küldenünk a Állapot. Hiba üzenetet küldő színésznek.

Ez nem történik meg automatikusan, ha a színész kivételt vet az üzenet és a kérdez() a hívás időtúllépés lesz, és a naplókban nem lesz utalás a kivételre:

@ Nyilvános információk felülbírálása A createReceive () {return ReceiveBuilder () .match (CountWords.class, r -> {try {int numberOfWords = countWordsFromLine (r.line); getSender (). Tell (numberOfWords, getSelf ());} catch (Ex kivétel) {getSender (). Tell (új akka.actor.Status.Failure (ex), getSelf ()); dob ex;}}). Build (); }

Nálunk is van előre() módszer, amely hasonló a Mondd(). A különbség az, hogy az üzenet eredeti feladóját megtartja az üzenet küldésekor, így az üzenetet továbbító szereplő csak közvetítő szereplőként működik:

printerActorRef.forward (új PrinterActor.PrintFinalResult (totalNumberOfWords), getContext ());

5.2. Üzenetek fogadása

Minden szereplő végrehajtja a createReceive () módszer, amely az összes bejövő üzenetet kezeli. A ReceiveBuilder () kapcsoló utasításként viselkedik, és megpróbálja a kapott üzenetet a megadott üzenetek típusához igazítani:

public fogadás createReceive () {return ReceiveBuilder (). matchEquals ("printit", p -> {System.out.println ("Ennek a szereplőnek a címe:" + getSelf ());}). build (); }

Fogadásakor egy üzenet kerül a FIFO sorba, így az üzeneteket egymás után kezeljük.

6. Színész megölése

Amikor befejeztük a színész használatát leállíthatjuk a álljon meg() módszer tól ActorRefFactory felület:

system.stop (myActorRef);

Használhatjuk ezt a módszert bármely gyermek színész vagy maga a színész megszüntetésére. Fontos megjegyezni, hogy a leállítás aszinkron módon történik, és hogy a az aktuális üzenetfeldolgozás befejeződik mielőtt a színészt megszüntetik. Több bejövő üzenetet nem fogadunk el a szereplő postafiókjában.

Által megállítani egy szülő színészt, gyilkossági jelet is küldünk az összes gyermek színésznek amelyeket arra szült.

Amikor már nincs szükségünk a színész rendszerre, leállíthatjuk az összes erőforrás felszabadítása és a memória szivárgásának megakadályozása érdekében:

Jövőbeni terminateResponse = system.terminate ();

Ez megállítja a rendszer őrző szereplőket, tehát az ebben az Akka rendszerben definiált összes szereplőt.

Küldhetnénk egy Mérgező tabletta üzenet minden színésznek, akit meg akarunk ölni:

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

A Mérgező tabletta üzenetet a színész minden más üzenethez hasonlóan megkapja, és sorba teszi. A színész az összes üzenetet addig dolgozza fel, amíg el nem jut a Mérgező tabletta egy. A színész csak akkor kezdi meg a felmondási folyamatot.

A színész meggyilkolásához használt másik különleges üzenet a Megöl üzenet. ellentétben a Mérgező tabletta, a színész dob egy ActorKilledException az üzenet feldolgozásakor:

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

7. Következtetés

Ebben a cikkben bemutattuk az Akka keretrendszer alapjait. Megmutattuk, hogyan lehet meghatározni a szereplőket, hogyan kommunikálnak egymással és hogyan lehet őket megszüntetni.

Az Akkával való együttműködés során néhány bevált gyakorlatot zárunk le:

  • használat Mondd() ahelyett kérdez() amikor a teljesítmény aggodalomra ad okot
  • Használat során kérdez() a kivételeket mindig a küldéssel kell kezelnünk Kudarc üzenet
  • a szereplőknek nem szabad megosztani semmilyen változtatható állapotot
  • egy színészt nem szabad más színészen belül nyilvánítani
  • a színészeket nem állítják le automatikusan amikor már nem hivatkoznak rájuk. Kifejezetten el kell pusztítanunk egy színészt, amikor már nincs rá szükségünk, hogy megakadályozzuk a memória szivárgását
  • a színészek által használt üzenetek mindig változhatatlannak kell lennie

Mint mindig, a cikk forráskódja elérhető a GitHubon.