Inleiding tot de lente met Akka

1. Inleiding

In dit artikel zullen we ons concentreren op de integratie van Akka met het Spring Framework - om injectie van op Spring gebaseerde services in Akka-actoren mogelijk te maken.

Voordat u dit artikel leest, wordt een voorkennis van de basisprincipes van Akka aanbevolen.

2. Afhankelijkheidsinjectie in Akka

Akka is een krachtig applicatieframework gebaseerd op het Actor concurrency-model. Het framework is geschreven in Scala, waardoor het natuurlijk ook volledig bruikbaar is in Java-gebaseerde applicaties. En dus het is heel vaak dat we Akka willen integreren met een bestaande Spring-gebaseerde applicatie of gebruik gewoon Spring om bonen in acteurs te bedraden.

Het probleem met Spring / Akka-integratie ligt in het verschil tussen het beheer van bonen in het voorjaar en het beheer van actoren in Akka: actoren hebben een specifieke levenscyclus die verschilt van de typische levenscyclus van Spring bean.

Bovendien worden actoren opgesplitst in een actor zelf (wat een intern implementatiedetail is en niet kan worden beheerd door Spring) en een actorreferentie, die toegankelijk is via een clientcode, en ook kan worden geserialiseerd en overgedragen tussen verschillende Akka-runtimes.

Gelukkig biedt Akka een mechanisme, namelijk Akka-extensies, dat het gebruik van externe afhankelijkheidsinjectiekaders een vrij gemakkelijke taak maakt.

3. Maven afhankelijkheden

Om het gebruik van Akka in ons Spring-project te demonstreren, hebben we een absoluut minimum aan Spring-afhankelijkheid nodig: de lente-context bibliotheek, en ook de akka-acteur bibliotheek. De bibliotheekversies kunnen worden uitgepakt naar het sectie van de pom:

 4.3.1.RELEASE 2.4.8 org.springframework spring-context $ {spring.version} com.typesafe.akka akka-actor_2.11 $ {akka.version} 

Controleer Maven Central voor de nieuwste versies van lente-context en akka-acteur afhankelijkheden.

En merk op hoe, dat de akka-acteur afhankelijkheid heeft een _2.11 postfix in zijn naam, wat aangeeft dat deze versie van Akka-framework is gebouwd tegen de Scala-versie 2.11. De corresponderende versie van de Scala-bibliotheek wordt tijdelijk in uw build opgenomen.

4. Spring Beans in Akka Actors injecteren

Laten we een eenvoudige Spring / Akka-applicatie maken die bestaat uit een enkele actor die de naam van een persoon kan beantwoorden door deze persoon te begroeten. De logica van begroeting wordt geëxtraheerd naar een aparte service. We willen deze service automatisch naar een actor-instantie sturen. De voorjaarsintegratie helpt ons bij deze taak.

4.1. Een acteur en een service definiëren

Om de injectie van een service in een acteur te demonstreren, maken we een eenvoudige klasse GroetActor gedefinieerd als een ongetypeerde acteur (verlenging van de Akka's Ongetypeerde acteur basisklasse). De belangrijkste methode van elke Akka-acteur is de onOntvang methode die een bericht ontvangt en verwerkt volgens een bepaalde logica.

In ons geval is de GroetActor implementatie controleert of het bericht van een vooraf gedefinieerd type is Begroeten, haalt vervolgens de naam van de persoon uit de Begroeten instantie, gebruikt vervolgens de GroetService om een ​​begroeting voor deze persoon te ontvangen en de afzender te beantwoorden met de ontvangen begroetingstring. Als het bericht van een ander onbekend type is, wordt het doorgegeven aan de vooraf gedefinieerde actor onbehandeld methode.

Laten we eens kijken:

@Component @Scope (ConfigurableBeanFactory.SCOPE_PROTOTYPE) openbare klasse GreetingActor breidt UntypedActor {privé GreetingService greetingService; // constructor @Override public void onReceive (Object bericht) gooit Throwable {if (bericht exemplaar van Greet) {String name = ((Greet) bericht) .getName (); getSender (). tell (groetService.greet (naam), getSelf ()); } else {onverwerkt (bericht); }} openbare statische klasse Greet {private String-naam; // standard constructors / getters}}

Merk op dat de Begroeten Het berichttype wordt gedefinieerd als een statische innerlijke klasse binnen deze actor, wat als een goede gewoonte wordt beschouwd. Geaccepteerde berichttypen moeten zo dicht mogelijk bij een actor worden gedefinieerd om verwarring te voorkomen over welke berichttypen deze actor kan verwerken.

Let ook op de lente-annotaties @Component en @Scope - deze definiëren de klasse als een door Spring beheerde boon met de voorlopig ontwerp reikwijdte.

De scope is erg belangrijk, omdat elk bean-retrieval-verzoek moet resulteren in een nieuw aangemaakte instantie, aangezien dit gedrag overeenkomt met Akka's actor-levenscyclus. Als je deze bean implementeert met een andere scope, zal het typische geval van het herstarten van acteurs in Akka hoogstwaarschijnlijk niet correct functioneren.

Merk ten slotte op dat we dit niet expliciet hoefden te doen @Autowire de GroetService bijvoorbeeld - dit is mogelijk dankzij de nieuwe functie van Spring 4.3 genaamd Impliciete constructorinjectie.

De implementatie van GreeterService is vrij eenvoudig, merk op dat we het hebben gedefinieerd als een door Spring beheerde boon door de @Component annotatie erop (met standaard singleton reikwijdte):

@Component public class GreetingService {public String greet (String name) {return "Hallo," + naam; }}

4.2. Spring Support toevoegen via Akka Extension

De eenvoudigste manier om Spring met Akka te integreren, is via een Akka-extensie.

Een extensie is een singleton-instantie die per actor-systeem wordt aangemaakt. Het bestaat zelf uit een extensieklasse, die de markerinterface implementeert Uitbreiding, en een extensie-id-klasse die gewoonlijk erft AbstractExtensionId.

Aangezien deze twee klassen nauw met elkaar zijn verbonden, is het logisch om de Uitbreiding klasse genest in de Extensie-id klasse:

openbare klasse SpringExtension breidt AbstractExtensionId uit {openbare statische laatste SpringExtension SPRING_EXTENSION_PROVIDER = nieuwe SpringExtension (); @Override openbare SpringExt createExtension (ExtendedActorSystem-systeem) {retourneer nieuwe SpringExt (); } openbare statische klasse SpringExt implementeert extensie {privé vluchtige ApplicationContext applicationContext; public void initialiseren (ApplicationContext applicationContext) {this.applicationContext = applicationContext; } public Props props (String actorBeanName) {return Props.create (SpringActorProducer.class, applicationContext, actorBeanName); }}}

EersteVoorjaarsuitbreiding implementeert een enkele createExtension methode van de AbstractExtensionId class - die verantwoordelijk is voor het maken van een extensie-instantie, de SpringExt voorwerp.

De Voorjaarsuitbreiding klasse heeft ook een statisch veld SPRING_EXTENSION_PROVIDER die een verwijzing bevat naar zijn enige exemplaar. Het is vaak logisch om een ​​privéconstructor toe te voegen om dat expliciet te vermelden SpringExtention hoort een singleton-klasse te zijn, maar voor de duidelijkheid laten we het weg.

ten tweede, de statische innerlijke klasse SpringExt is de extensie zelf. Net zo Uitbreiding is gewoon een markeringsinterface, we kunnen de inhoud van deze klasse naar eigen inzicht definiëren.

In ons geval hebben we de initialiseren methode voor het houden van een veer ApplicationContext instantie - deze methode wordt slechts één keer aangeroepen per initialisatie van de extensie.

We hebben ook de rekwisieten methode voor het maken van een Rekwisieten voorwerp. Rekwisieten instantie is een blauwdruk voor een acteur, en in ons geval de Props. Creëren methode ontvangt een SpringActorProducer class- en constructorargumenten voor deze klasse. Dit zijn de argumenten waarmee de constructor van deze klasse zal worden aangeroepen.

De rekwisieten methode wordt elke keer uitgevoerd als we een door Spring beheerde actorreferentie nodig hebben.

De derde en het laatste stukje van de puzzel is de SpringActorProducer klasse. Het implementeert Akka's IndirectActorProducer interface waarmee het instantiatieproces voor een actor kan worden overschreven door het produceren en actorClass methoden.

Zoals je waarschijnlijk al geraden hebt, in plaats van directe instantiatie haalt het altijd een actorinstantie op uit de Spring’s ApplicationContext. Omdat we de acteur a hebben gemaakt voorlopig ontwerp-scoped bean, elke oproep aan de produceren methode retourneert een nieuw exemplaar van de actor:

openbare klasse SpringActorProducer implementeert IndirectActorProducer {private ApplicationContext applicationContext; privé String beanActorName; openbare SpringActorProducer (ApplicationContext applicationContext, String beanActorName) {this.applicationContext = applicationContext; this.beanActorName = beanActorName; } @Override public Actor produce () {return (Actor) applicationContext.getBean (beanActorName); } @Override public Class actorClass () {return (Class) applicationContext .getType (beanActorName); }}

4.3. Alles samenvoegen

Het enige dat u nog hoeft te doen, is een Spring-configuratieklasse maken (gemarkeerd met @Configuratie annotatie) die Spring vertelt om het huidige pakket samen met alle geneste pakketten te scannen (dit wordt gegarandeerd door het @BuienRadarNL annotatie) en maak een Spring-container.

We hoeven maar één extra boon toe te voegen - de ActorSystem instantie - en initialiseer de Spring-extensie hierop ActorSystem:

@Configuration @ComponentScan openbare klasse AppConfiguration {@Autowired privé ApplicationContext applicationContext; @Bean openbaar ActorSystem actorSystem () {ActorSystem system = ActorSystem.create ("akka-spring-demo"); SPRING_EXTENSION_PROVIDER.get (systeem) .initialize (applicationContext); retoursysteem; }}

4.4. Acteurs met veerkracht ophalen

Om te testen of alles correct werkt, kunnen we de ActorSystem instantie in onze code (ofwel een door Spring beheerde applicatiecode of een op Spring gebaseerde test), maak een Rekwisieten object voor een actor die onze extensie gebruikt, haal een verwijzing naar een actor op via Rekwisieten bezwaar maken en proberen iemand te begroeten:

ActorRef greeter = system.actorOf (SPRING_EXTENSION_PROVIDER.get (systeem) .props ("greetingActor"), "greeter"); FiniteDuration-duur = FiniteDuration.create (1, TimeUnit.SECONDS); Timeout timeout = Timeout.durationToTimeout (duur); Toekomstig resultaat = vragen (begroeting, nieuwe begroeting ("Jan"), time-out); Assert.assertEquals ("Hallo, John", Await.result (resultaat, duur));

Hier gebruiken we de typische akka.pattern.Patterns.ask patroon dat een Scala's retourneert Toekomst voorbeeld. Zodra de berekening is voltooid, wordt het Toekomst wordt opgelost met een waarde die we hebben geretourneerd in onze GreetingActor.onMessasge methode.

Ofwel wachten we op het resultaat door de Scala's toe te passen Wacht. Resultaat methode naar de Toekomst, of, meer bij voorkeur, bouw de volledige applicatie met asynchrone patronen.

5. Conclusie

In dit artikel hebben we laten zien hoe je Spring Framework met Akka en autowire beans in acteurs kunt integreren.

De broncode voor het artikel is beschikbaar op GitHub.