Úvod do hercov Akka v Jave

1. Úvod

Akka je open-source knižnica, ktorá pomáha ľahko vyvíjať súbežné a distribuované aplikácie pomocou Javy alebo Scaly využitím hercovho modelu.

V tomto návode predstavíme základné funkcie, ako je definovanie aktérov, ako komunikujú a ako ich môžeme zabiť. V záverečných poznámkach si tiež všimneme niektoré osvedčené postupy pri práci s Akkou.

2. Herecký model

Herecký model nie je pre komunitu informatiky nový. Prvýkrát ho predstavil Carl Eddie Hewitt v roku 1973 ako teoretický model pre spracovanie súbežných výpočtov.

Svoju praktickú použiteľnosť začala ukazovať, keď si softvérový priemysel začal uvedomovať úskalia implementácie súbežných a distribuovaných aplikácií.

Herec predstavuje nezávislú výpočtovú jednotku. Niektoré dôležité vlastnosti sú:

  • aktér zapuzdruje svoj stav a časť aplikačnej logiky
  • aktéri interagujú iba prostredníctvom asynchrónnych správ a nikdy nie prostredníctvom priamych volaní metód
  • každý účastník má jedinečnú adresu a poštovú schránku, do ktorej môžu iní aktéri doručovať správy
  • herec spracuje všetky správy v poštovej schránke v postupnom poradí (predvolená implementácia poštovej schránky je front FIFO)
  • systém aktérov je organizovaný v stromovej hierarchii
  • herec môže vytvárať ďalších hercov, môže posielať správy ktorémukoľvek inému hercovi a zastaviť sa, alebo ktorýkoľvek herec vytvoril

2.1. Výhody

Vývoj súbežných aplikácií je zložitý, pretože sa musíme zaoberať synchronizáciou, zámkami a zdieľanou pamäťou. Pomocou hercov Akka môžeme ľahko písať asynchrónny kód bez potreby zámkov a synchronizácie.

Jednou z výhod použitia správy namiesto volania metód je tá vlákno odosielateľa nebude blokovať čakanie na návratovú hodnotu, keď pošle správu inému aktérovi. Prijímajúci herec odpovie výsledkom zaslaním správy s odpoveďou odosielateľovi.

Ďalšou veľkou výhodou používania správ je, že sa nemusíme starať o synchronizáciu v prostredí s viacerými vláknami. Je to tak kvôli skutočnosti všetky správy sa spracovávajú postupne.

Ďalšou výhodou modelu herca Akka je spracovanie chýb. Organizovaním aktérov v hierarchii môže každý aktér upozorniť svojho rodiča na zlyhanie, aby mohol podľa toho konať. Rodičovský herec sa môže rozhodnúť zastaviť alebo znovu spustiť detských hercov.

3. Inštalácia

Aby sme mohli využiť výhody hercov Akky, musíme pridať nasledujúcu závislosť od Maven Central:

 com.typesafe.akka akka-herec_2.12 2.5.11 

4. Vytvorenie herca

Ako už bolo spomenuté, aktéri sú definovaní v hierarchickom systéme. Všetci aktéri, ktorí zdieľajú spoločnú konfiguráciu, budú definovaní pomocou ActorSystem.

Zatiaľ jednoducho definujeme znak ActorSystem s predvolenou konfiguráciou a vlastným menom:

ActorSystem system = ActorSystem.create ("testovací systém"); 

Aj keď sme ešte nevytvorili žiadnych hercov, systém už bude obsahovať 3 hlavných aktérov:

  • aktér koreňového strážcu s adresou „/“, ktorá ako názvy uvádza koreň hierarchie systému aktéra
  • aktér zákonného zástupcu používateľa s adresou „/ užívateľ“. Bude to rodič všetkých aktérov, ktorých definujeme
  • aktér systémového opatrovníka s adresou „/ systém“. Bude to rodič pre všetkých aktérov definovaných interne systémom Akka

Každý herec Akka predĺži AbstractActor abstraktná trieda a implementovať createReceive () spôsob spracovania prichádzajúcich správ od iných aktérov:

public class MyActor extends AbstractActor {public Receive createReceive () {return receiveBuilder (). build (); }}

Toto je najzákladnejší herec, ktorého môžeme vytvoriť. Môže prijímať správy od iných aktérov a zahodí ich, pretože v systéme nie sú definované žiadne zodpovedajúce vzory správ ReceiveBuilder. O párovaní vzorov správ si povieme neskôr v tomto článku.

Teraz, keď sme vytvorili nášho prvého herca, by sme ho mali zahrnúť do ActorSystem:

ActorRef readingActorRef = system.actorOf (Props.create (MyActor.class), "môj herec");

4.1. Konfigurácia herca

The Rekvizity trieda obsahuje konfiguráciu aktéra. Môžeme nakonfigurovať napríklad dispečer, poštovú schránku alebo konfiguráciu nasadenia. Táto trieda je nemenná, a teda bezpečná pre vlákna, takže ju možno zdieľať pri vytváraní nových hercov.

Dôrazne sa odporúča a považuje sa za osvedčený postup na definovanie továrenských metód vo vnútri objektu herec, ktorý bude spracovávať vytvorenie súboru Rekvizity objekt.

Na príklade definujme herca, ktorý bude robiť nejaké spracovanie textu. Herec dostane a String objekt, na ktorom vykoná spracovanie:

verejná trieda ReadingActor rozširuje AbstractActor {private String text; public static Rekvizity rekvizity (text reťazca) {návrat Props.create (ReadingActor.class, text); } // ...}

Teraz na vytvorenie inštancie tohto typu herca stačí použiť znak rekvizity () továrenská metóda na odovzdanie String argument pre konštruktéra:

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

Teraz, keď vieme, ako definovať herca, pozrime sa, ako komunikujú vo vnútri hercovho systému.

5. Herecké správy

Aktéri môžu vzájomne komunikovať a odosielať a prijímať správy od ktoréhokoľvek iného aktéra v systéme. Títo správ môže byť akýkoľvek typ objektu s podmienkou, že je nemenný.

Osvedčeným postupom je definovať správy vo vnútri triedy hercov. To pomáha písať kód, ktorý je ľahko pochopiteľný a vie, aké správy môže herec zvládnuť.

5.1. Posielanie správ

Vo vnútri systému herec Akka sa správy odosielajú pomocou metód:

  • povedz ()
  • opýtať sa()
  • dopredu ()

Ak chceme poslať správu a neočakávame odpoveď, môžeme použiť povedz () metóda. Toto je najefektívnejšia metóda z hľadiska výkonu:

readingActorRef.tell (nový ReadingActor.ReadLines (), ActorRef.noSender ()); 

Prvý parameter predstavuje správu, ktorú posielame na adresu aktéra readingActorRef.

Druhý parameter určuje, kto je odosielateľ. Je to užitočné, keď aktér prijímajúci správu potrebuje poslať odpoveď inému aktérovi, ako je odosielateľ (napríklad rodič vysielajúceho aktéra).

Spravidla môžeme druhý parameter nastaviť na nulový alebo ActorRef.noSender (), pretože odpoveď neočakávame. Ak potrebujeme od herca odpoveď, môžeme použiť opýtať sa() metóda:

CompletableFuture future = ask (wordCounterActorRef, nový WordCounterActor.CountWords (riadok), 1000) .toCompletableFuture ();

Pri žiadosti o odpoveď od herca a DokončenieStage objekt je vrátený, takže spracovanie zostáva neblokujúce.

Veľmi dôležitou skutočnosťou, ktorej musíme venovať pozornosť, je riešenie chýb v rámci aktéra, ktorý bude reagovať. Ak chcete vrátiť a Budúcnosť objekt, ktorý bude obsahovať výnimku, ktorú musíme poslať a Stav. Zlyhanie správa odosielateľovi.

Toto sa nerobí automaticky, keď herec pri spracovaní správy a opýtať sa() hovoru vyprší časový limit a v protokoloch sa nezobrazí odkaz na výnimku:

@Override public Prijať createReceive () {return receiveBuilder () .match (CountWords.class, r -> {try {int numberOfWords = countWordsFromLine (r.line); getSender (). Tell (numberOfWords, getSelf ());} chytiť (Výnimka ex) {getSender (). Tell (new akka.actor.Status.Failure (ex), getSelf ()); throw ex;}}). Build (); }

Máme tiež dopredu () metóda, ktorá je podobná povedz (). Rozdiel je v tom, že pôvodný odosielateľ správy sa pri odosielaní správy zachová, takže aktér preposielajúci správu funguje iba ako sprostredkovateľ:

printerActorRef.forward (nový PrinterActor.PrintFinalResult (totalNumberOfWords), getContext ());

5.2. Príjem správ

Každý aktér bude implementovať createReceive () metóda, ktorý spracováva všetky prichádzajúce správy. The receiveBuilder () funguje ako príkaz switch, snaží sa priradiť prijatú správu k typu definovaných správ:

public Receive createReceive () {return receiveBuilder (). matchEquals ("printit", p -> {System.out.println ("Adresa tohto aktéra je:" + getSelf ());}). build (); }

Po prijatí sa správa zaradí do frontu FIFO, takže sa so správami zaobchádza postupne.

6. Zabitie herca

Keď sme skončili s používaním herca môžeme to zastaviť zavolaním na stop () metóda z ActorRefFactory rozhranie:

system.stop (myActorRef);

Túto metódu môžeme použiť na ukončenie ktoréhokoľvek detského herca alebo samotného herca. Je dôležité poznamenať, že zastavenie sa deje asynchrónne a že spracovanie aktuálnej správy sa ukončí pred ukončením účinkovania herca. Do schránky herca nebudú prijímané žiadne ďalšie prichádzajúce správy.

Autor: zastavenie rodičovského herca, pošleme tiež signál zabitia všetkým detským hercom ktoré z toho vznikli.

Keď už systém herec nepotrebujeme, môžeme ho ukončiť, aby sme uvoľnili všetky zdroje a zabránili úniku pamäte:

Budúca terminateResponse = system.terminate ();

Toto zastaví aktérov strážcu systému, a teda všetkých aktérov definovaných v tomto systéme Akka.

Mohli by sme poslať aj PoisonPill správa každému hercovi, ktorého chceme zabiť:

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

The PoisonPill správa bude aktérom prijatá ako každá iná správa a zaradená do poradia. Herec spracuje všetky správy, kým sa nedostane k PoisonPill jeden. Až potom začne herec proces ukončenia.

Ďalšou špeciálnou správou použitou na zabitie herca je Zabiť správa. Na rozdiel od PoisonPill, herec nahodí ActorKilledException pri spracovaní tejto správy:

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

7. Záver

V tomto článku sme predstavili základy rámca Akka. Ukázali sme, ako definovať aktérov, ako medzi sebou komunikujú a ako ich ukončiť.

Na záver zakončíme niekoľkými osvedčenými postupmi pri práci s Akkou:

  • použitie povedz () namiesto opýtať sa() keď výkon znepokojuje
  • pri použití opýtať sa() výnimky by sme mali vždy vybaviť zaslaním a Zlyhanie správa
  • aktéri by nemali zdieľať žiadny premenlivý stav
  • herec by nemal byť deklarovaný v rámci iného herca
  • herci sa nezastavujú automaticky keď sa na ne už nebude odkazovať. Ak už herca nepotrebujeme, musíme ho výslovne zničiť, aby sme zabránili úniku pamäte
  • správy používané aktérmi by mali byť vždy nemenné

Zdrojový kód článku je ako vždy k dispozícii na stránkach GitHub.