Einführung in den Frühling mit Akka

Einführung in den Frühling mit Akka

1. Einführung

In diesem Artikel konzentrieren wir uns auf die Integration von Akka in das Spring Framework, um die Einführung von Spring-basierten Diensten in Akka-Akteure zu ermöglichen.

Bevor Sie diesen Artikel lesen, sollten Sie sich mit den Grundlagen von Akka vertraut machen.

Weitere Lektüre:

Einführung in Akka Actors in Java

Erfahren Sie, wie Sie mit Akka Actors in Java gleichzeitige und verteilte Anwendungen erstellen.

Read more

Führer zu den Akka Bächen

Eine schnelle und praktische Anleitung zur Datenstromumwandlung in Java mithilfe der Akka Streams-Bibliothek.

Read more

Spring Boot und Kotlin

Erfahren Sie, wie Sie Kotlin zusammen mit Spring Boot 2.x verwenden.

Read more

2. Abhängigkeitsinjektion in Akka

Akka ist ein leistungsstarkes Anwendungsframework, das auf dem Actor-Parallelitätsmodell basiert. Das Framework ist in Scala geschrieben, was es natürlich auch in Java-basierten Anwendungen voll nutzbar macht. Und soit’s very often we will want to integrate Akka with an existing Spring-based application oder verwenden Sie einfach Spring, um Bohnen mit Schauspielern zu verbinden.

Das Problem bei der Integration von Spring / Akka liegt im Unterschied zwischen der Verwaltung von Bohnen im Frühjahr und der Verwaltung von Akteuren in Akka:actors have a specific lifecycle that differs from typical Spring bean lifecycle.

Darüber hinaus sind die Akteure in einen Akteur selbst (ein internes Implementierungsdetail, das nicht von Spring verwaltet werden kann) und eine Akteurreferenz unterteilt, auf die über einen Clientcode zugegriffen werden kann. Sie kann auch zwischen verschiedenen Akka-Laufzeiten serialisiert und portiert werden.

Glücklicherweise bietet Akka einen Mechanismus, nämlichAkka extensions, der die Verwendung externer Abhängigkeitsinjektions-Frameworks zu einer ziemlich einfachen Aufgabe macht.

3. Maven-Abhängigkeiten

Um die Verwendung von Akka in unserem Spring-Projekt zu demonstrieren, benötigen wir eine minimale Spring-Abhängigkeit - diespring-context-Bibliothek und auch dieakka-actor-Bibliothek. Die Bibliotheksversionen können in den Abschnitt<properties> vonpom extrahiert werden:


    4.3.1.RELEASE
    2.4.8



    
        org.springframework
        spring-context
        ${spring.version}
    

    
        com.typesafe.akka
        akka-actor_2.11
        ${akka.version}
    

Stellen Sie sicher, dass Sie in Maven Central nach den neuesten Versionen der Abhängigkeitenspring-context undakka-actor suchen.

Beachten Sie auch, dass die Abhängigkeit vonakka-actoreinen Postfix von_2.11enthält, was bedeutet, dass diese Version des Akka-Frameworks gegen die Scala-Version 2.11 erstellt wurde. Die entsprechende Version der Scala-Bibliothek wird transitiv in Ihren Build aufgenommen.

4. Injizieren von Frühlingsbohnen in Akka-Schauspieler

Erstellen wir eine einfache Spring / Akka-Anwendung, die aus einem einzelnen Akteur besteht und auf den Namen einer Person antworten kann, indem diese Person begrüßt wird. Die Logik der Begrüßung wird in einen separaten Dienst extrahiert. Wir werden diesen Dienst automatisch mit einer Schauspielerinstanz verbinden wollen. Spring Integration wird uns bei dieser Aufgabe helfen.

4.1. Definieren eines Schauspielers und eines Dienstes

Um die Injektion eines Dienstes in einen Akteur zu demonstrieren, erstellen wir eine einfache KlasseGreetingActor, die als untypisierter Akteur definiert ist (Erweiterung der BasisklasseUntypedActorvon Akka). Die Hauptmethode jedes Akka-Akteurs ist dieonReceive-Methode, die eine Nachricht empfängt und gemäß einer bestimmten Logik verarbeitet.

In unserem Fall prüft die Implementierung vonGreetingActor, ob die Nachricht vom vordefinierten TypGreet ist, nimmt dann den Namen der Person aus der Instanz vonGreetund verwendet dannGreetingService um eine Begrüßung für diese Person zu erhalten und antwortet dem Absender mit der empfangenen Begrüßungszeichenfolge. Wenn die Nachricht von einem anderen unbekannten Typ ist, wird sie an die vordefinierteunhandled-Methode des Akteurs übergeben.

Werfen wir einen Blick:

@Component
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public class GreetingActor extends UntypedActor {

    private GreetingService greetingService;

    // constructor

    @Override
    public void onReceive(Object message) throws Throwable {
        if (message instanceof Greet) {
            String name = ((Greet) message).getName();
            getSender().tell(greetingService.greet(name), getSelf());
        } else {
            unhandled(message);
        }
    }

    public static class Greet {

        private String name;

        // standard constructors/getters

    }
}

Beachten Sie, dass der NachrichtentypGreetals statische innere Klasse in diesem Akteur definiert ist, was als bewährte Methode angesehen wird. Akzeptierte Nachrichtentypen sollten so nah wie möglich an einem Akteur definiert werden, um Verwirrung darüber zu vermeiden, welche Nachrichtentypen dieser Akteur verarbeiten kann.

Also notice the Spring annotations @Component and @Scope - Diese definieren die Klasse als Spring-verwaltete Bean mit dem Gültigkeitsbereich vonprototype.

Der Umfang ist sehr wichtig, da jede Bean-Abrufanforderung zu einer neu erstellten Instanz führen sollte, da dieses Verhalten dem Akka-Akteur-Lebenszyklus entspricht. Wenn Sie dieses Bean mit einem anderen Gültigkeitsbereich implementieren, funktioniert der typische Fall des Neustarts von Akteuren in Akka höchstwahrscheinlich falsch.

Beachten Sie schließlich, dass wir@Autowire nicht explizitGreetingService Instanz müssen - dies ist aufgrund der neuen Funktion von Spring 4.3 namensImplicit Constructor Injection möglich.

Die Implementierung vonGreeterService ist ziemlich einfach. Beachten Sie, dass wir es als Spring-verwaltete Bean definiert haben, indem wir die Annotation@Componenthinzugefügt haben (mit dem Standardbereich vonsingleton):

@Component
public class GreetingService {

    public String greet(String name) {
        return "Hello, " + name;
    }
}

4.2. Hinzufügen der Federunterstützung über die Akka-Erweiterung

Der einfachste Weg, Spring in Akka zu integrieren, ist eine Akka-Erweiterung.

An extension is a singleton instance created per actor system. Es besteht aus einer Erweiterungsklasse selbst, die die MarkierungsschnittstelleExtension implementiert, und einer Erweiterungs-ID-Klasse, die normalerweiseAbstractExtensionId erbt.

Da diese beiden Klassen eng miteinander verbunden sind, ist es sinnvoll, die KlasseExtensionzu implementieren, die in der KlasseExtensionIdverschachtelt ist:

public class SpringExtension
  extends AbstractExtensionId {

    public static final SpringExtension SPRING_EXTENSION_PROVIDER
      = new SpringExtension();

    @Override
    public SpringExt createExtension(ExtendedActorSystem system) {
        return new SpringExt();
    }

    public static class SpringExt implements Extension {
        private volatile ApplicationContext applicationContext;

        public void initialize(ApplicationContext applicationContext) {
            this.applicationContext = applicationContext;
        }

        public Props props(String actorBeanName) {
            return Props.create(
              SpringActorProducer.class, applicationContext, actorBeanName);
        }
    }
}

First -SpringExtension implementiert eine einzelnecreateExtension-Methode aus derAbstractExtensionId-Klasse, die die Erstellung einer Erweiterungsinstanz, desSpringExt-Objekts, berücksichtigt.

Die KlasseSpringExtension hat auch ein statisches FeldSPRING_EXTENSION_PROVIDER, das einen Verweis auf ihre einzige Instanz enthält. Es ist oft sinnvoll, einen privaten Konstruktor hinzuzufügen, um explizit anzugeben, dassSpringExtention eine Singleton-Klasse sein soll, aber wir werden es aus Gründen der Übersichtlichkeit weglassen.

Secondly, die statische innere KlasseSpringExt ist die Erweiterung selbst. DaExtension lediglich eine Markierungsschnittstelle ist, können wir den Inhalt dieser Klasse nach Belieben definieren.

In unserem Fall benötigen wir die Methodeinitialize, um die Instanz von SpringApplicationContextbeizubehalten. Diese Methode wird nur einmal pro Initialisierung der Erweiterung aufgerufen.

Außerdem benötigen wir die Methodeprops, um ein ObjektPropszu erstellen. Die Instanz vonPropsist eine Blaupause für einen Akteur, und in unserem Fall empfängt die MethodeProps.createdie Klassen- und Konstruktorargumente vonSpringActorProducerfür diese Klasse. Dies sind die Argumente, mit denen der Konstruktor dieser Klasse aufgerufen wird.

Dieprops-Methode wird jedes Mal ausgeführt, wenn wir eine von Spring verwaltete Schauspielerreferenz benötigen.

The third und das letzte Puzzleteil ist die KlasseSpringActorProducer. Es implementiert dieIndirectActorProducer-Schnittstelle von Akka, mit der der Instanziierungsprozess für einen Akteur durch Implementieren der Methodenproduce undactorClassüberschrieben werden kann.

Wie Sie wahrscheinlich bereits erraten haben,instead of direct instantiation, it will always retrieve an actor instance from the Spring’s ApplicationContext. Da wir den Akteur zu einer Bean mitprototype-Bereich gemacht haben, gibt jeder Aufruf der Methodeproduce eine neue Instanz des Akteurs zurück:

public class SpringActorProducer implements IndirectActorProducer {

    private ApplicationContext applicationContext;

    private String beanActorName;

    public 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 zusammenfügen

Sie müssen nur noch eine Spring-Konfigurationsklasse erstellen (gekennzeichnet mit der Annotation@Configuration), die Spring anweist, das aktuelle Paket zusammen mit allen verschachtelten Paketen zu scannen (dies wird durch die Annotation@ComponentScanichergestellt ) und erstellen Sie einen Federbehälter.

Wir müssen nur eine einzige zusätzliche Bean hinzufügen - die InstanzActorSystem- und die Spring-Erweiterung für dieseActorSystem initialisieren:

@Configuration
@ComponentScan
public class AppConfiguration {

    @Autowired
    private ApplicationContext applicationContext;

    @Bean
    public ActorSystem actorSystem() {
        ActorSystem system = ActorSystem.create("akka-spring-demo");
        SPRING_EXTENSION_PROVIDER.get(system)
          .initialize(applicationContext);
        return system;
    }
}

4.4. Abrufen von federverdrahteten Akteuren

Um zu testen, ob alles korrekt funktioniert, können wir die Instanz vonActorSystemin unseren Code einfügen (entweder einen von Spring verwalteten Anwendungscode oder einen Spring-basierten Test) und mit unserem einProps-Objekt für einen Akteur erstellen Erweiterung, rufen Sie einen Verweis auf einen Akteur über das Objekt vonPropsab und versuchen Sie, jemanden zu begrüßen:

ActorRef greeter = system.actorOf(SPRING_EXTENSION_PROVIDER.get(system)
  .props("greetingActor"), "greeter");

FiniteDuration duration = FiniteDuration.create(1, TimeUnit.SECONDS);
Timeout timeout = Timeout.durationToTimeout(duration);

Future result = ask(greeter, new Greet("John"), timeout);

Assert.assertEquals("Hello, John", Await.result(result, duration));


Hier verwenden wir das typischeakka.pattern.Patterns.ask-Muster, das dieFuture-Instanz einer Scala zurückgibt. Sobald die Berechnung abgeschlossen ist, wirdFuture mit einem Wert aufgelöst, den wir in unsererGreetingActor.onMessasge-Methode zurückgegeben haben.

Wir können entweder auf das Ergebnis warten, indem wir dieAwait.result-Methode der Scala auf dieFuture anwenden, oder, bevorzugter, die gesamte Anwendung mit asynchronen Mustern erstellen.

5. Fazit

In diesem Artikel haben wir gezeigt, wie Spring Framework mit Akka- und Autowire-Beans in Schauspieler integriert werden kann.

Der Quellcode für den Artikel iston GitHub verfügbar.