Хорошая идея - объявить инжектор и передать его в качестве параметра состояния при интеграции типизированного Akka Actor с Spring? - PullRequest
1 голос
/ 04 августа 2020

Мне не удалось найти удобный способ интегрировать Spring с типизированными Актерами. Вместо использования расширений я объявил службу инжектора

import akka.actor.typed.ActorSystem;
import com.akka.demo.a010.ASimpleSpringService;
import com.akka.demo.a010.AkkaSimpleBehaviour;
import lombok.Data;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
@Data
public class ActorInjector {

    @Autowired
    private ASimpleSpringService aSimpleSpringService;

    @Autowired
    private ActorInjector _self;

    public ActorSystem<AkkaSimpleBehaviour.Command> createAkkaSimpleBehaviour(String actorName) {
        return ActorSystem.create(AkkaSimpleBehaviour.create(_self), actorName);
    }

}

Эта служба автоматически подключается и передает эту ссылку простому субъекту.

Определение моего актера выглядит следующим образом.

import akka.actor.typed.ActorRef;
import akka.actor.typed.Behavior;
import akka.actor.typed.javadsl.AbstractBehavior;
import akka.actor.typed.javadsl.ActorContext;
import akka.actor.typed.javadsl.Behaviors;
import akka.actor.typed.javadsl.Receive;
import com.akka.demo.a010.config.ActorInjector;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;

@Slf4j
public class AkkaSimpleBehaviour extends AbstractBehavior<AkkaSimpleBehaviour.Command> {

    private final List<String> messages = new ArrayList<>();
    private ActorRef<List<String>> sender;
    private ActorInjector actorInjector;

    public interface Command extends Serializable {

    }

    @AllArgsConstructor
    public static class TellMeSomething implements Command {

        private static final long serialVersionUID = -7796709831949054890L;
        @Getter
        private final String message;
    }

    @AllArgsConstructor
    public static class CollectTheResults implements Command {

        private static final long serialVersionUID = 1643210899551075153L;
        @Getter
        private final ActorRef<List<String>> sender;
    }

    private AkkaSimpleBehaviour(ActorContext<Command> context, ActorInjector actorInjector) {
        super(context);
        this.actorInjector = actorInjector;
    }

    public static Behavior<Command> create(ActorInjector actorInjector) {
        return Behaviors.setup(ctx -> new AkkaSimpleBehaviour(ctx,actorInjector));
    }

    @Override
    public Receive<Command> createReceive() {
        return newReceiveBuilder().onMessage(TellMeSomething.class, message -> {
            messages.add(message.getMessage());
            actorInjector.getASimpleSpringService().logSomething("*-*-*-*-*-*-*-*-*-*");
            return Behaviors.same();
        }).onMessage(CollectTheResults.class, message -> {
            this.sender = message.getSender();
            if (messages.size() == 4) {
                this.sender.tell(messages);
            }
            return Behaviors.same();
        }).build();
    }
}

После прохождения службы инжектора я могу получить свои автоматические зависимости от этой службы, например:

actorInjector.getASimpleSpringService().logSomething("*-*-*-*-*-*-*-*-*-*");

Мой ASimpleService - это просто фиктивная служба, которая регистрирует вывод.

import org.springframework.stereotype.Service;

import lombok.extern.slf4j.Slf4j;

@Service
@Slf4j
public class ASimpleSpringService {

    public void logSomething(String message){
        log.info(message);
    }
}

Затем в простом RestController я использую систему следующим образом:

import akka.actor.typed.ActorSystem;
import akka.actor.typed.javadsl.AskPattern;
import com.akka.demo.a010.config.ActorInjector;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import java.time.Duration;
import java.util.List;
import java.util.concurrent.CompletionStage;
import java.util.concurrent.ExecutionException;

@RestController
@Slf4j
public class MyRestController {

    @Autowired
    private ActorInjector actorInjector;

    @GetMapping("/hello-akka")
    public void greetings() {
        ActorSystem<AkkaSimpleBehaviour.Command> exampleActor = actorInjector.createAkkaSimpleBehaviour("anActor");
        exampleActor.tell(new AkkaSimpleBehaviour.TellMeSomething("hello"));
        exampleActor.tell(new AkkaSimpleBehaviour.TellMeSomething("Who are you"));
        exampleActor.tell(new AkkaSimpleBehaviour.TellMeSomething("create a child"));
        exampleActor.tell(new AkkaSimpleBehaviour.TellMeSomething("Here is some message"));

        CompletionStage<List<String>> result = AskPattern.ask(exampleActor, AkkaSimpleBehaviour.CollectTheResults::new, Duration.ofSeconds(10), exampleActor.scheduler());
        result.whenComplete((reply, failure) -> {
            if(reply != null){
                log.info("The system responds in time");
            } else {
                log.error("The system does not respond in time");
                exampleActor.terminate();
                throw new RuntimeException("The system does not respond in time");
            }
            exampleActor.terminate();
        });
        try{
            List<String> messages = result.toCompletableFuture().get();
            messages.forEach(log::info);
        } catch (InterruptedException | ExecutionException e){
            e.printStackTrace();
        }
    }

}

Мой вопрос: я планирую поместить все свои службы в службу ActorInjector, и эти службы будут внедрены в моих актеров. Я не знаком с состояниями Akka и их побочными эффектами, но знаю, что хранить все эти одноэлементные службы в виде состояний актора - плохая идея. Является ли хорошей идеей хранить эти службы в качестве параметра актора? Какие побочные эффекты я могу испытать, поступая таким образом? Можешь указать мне дорогу?

...