Мне не удалось найти удобный способ интегрировать 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 и их побочными эффектами, но знаю, что хранить все эти одноэлементные службы в виде состояний актора - плохая идея. Является ли хорошей идеей хранить эти службы в качестве параметра актора? Какие побочные эффекты я могу испытать, поступая таким образом? Можешь указать мне дорогу?