作为 FSM 的行为

对于非类型化的 Actor,有明确的支持来构建「有限状态机」。在 Akka 类型中不需要支持,因为用行为表示 FSM 很简单。

为了了解如何使用 Akka 类型的 API 来建模 FSM,下面是从「非类型化的 Actor FSM 文档」移植的Buncher示例。它演示了如何:

  • 使用不同行为模拟状态
  • 通过将行为表示为一种方法,在每个状态下存储数据的模型
  • 实现状态超时

FSM 可以接收的事件为 Actor 可以接收的消息类型:

  1. interface Event {}
  2. static final class SetTarget implements Event {
  3. private final ActorRef<Batch> ref;
  4. public SetTarget(ActorRef<Batch> ref) {
  5. this.ref = ref;
  6. }
  7. public ActorRef<Batch> getRef() {
  8. return ref;
  9. }
  10. }
  11. final class Timeout implements Event {}
  12. static final Timeout TIMEOUT = new Timeout();
  13. public enum Flush implements Event {
  14. FLUSH
  15. }
  16. static final class Queue implements Event {
  17. private final Object obj;
  18. public Queue(Object obj) {
  19. this.obj = obj;
  20. }
  21. public Object getObj() {
  22. return obj;
  23. }
  24. }

启动它需要SetTarget,为要传递的Batches设置目标;Queue将添加到内部队列,而Flush将标记突发的结束。

非类型化 FSM 也有一个D(数据)类型参数。Akka 类型化不需要知道这一点,它可以通过将你的行为定义为方法来存储。

  1. interface Data {}
  2. final class Todo implements Data {
  3. private final ActorRef<Batch> target;
  4. private final List<Object> queue;
  5. public Todo(ActorRef<Batch> target, List<Object> queue) {
  6. this.target = target;
  7. this.queue = queue;
  8. }
  9. public ActorRef<Batch> getTarget() {
  10. return target;
  11. }
  12. public List<Object> getQueue() {
  13. return queue;
  14. }
  15. @Override
  16. public String toString() {
  17. return "Todo{" + "target=" + target + ", queue=" + queue + '}';
  18. }
  19. public Todo addElement(Object element) {
  20. List<Object> nQueue = new LinkedList<>(queue);
  21. nQueue.add(element);
  22. return new Todo(this.target, nQueue);
  23. }
  24. public Todo copy(List<Object> queue) {
  25. return new Todo(this.target, queue);
  26. }
  27. public Todo copy(ActorRef<Batch> target) {
  28. return new Todo(target, this.queue);
  29. }
  30. }

每个状态都会变成一种不同的行为。不需要显式goto,因为 Akka 类型已经要求你返回下一个行为。

  1. // FSM states represented as behaviors
  2. private static Behavior<Event> uninitialized() {
  3. return Behaviors.receive(Event.class)
  4. .onMessage(
  5. SetTarget.class,
  6. (context, message) -> idle(new Todo(message.getRef(), Collections.emptyList())))
  7. .build();
  8. }
  9. private static Behavior<Event> idle(Todo data) {
  10. return Behaviors.receive(Event.class)
  11. .onMessage(Queue.class, (context, message) -> active(data.addElement(message)))
  12. .build();
  13. }
  14. private static Behavior<Event> active(Todo data) {
  15. return Behaviors.withTimers(
  16. timers -> {
  17. // State timeouts done with withTimers
  18. timers.startSingleTimer("Timeout", TIMEOUT, Duration.ofSeconds(1));
  19. return Behaviors.receive(Event.class)
  20. .onMessage(Queue.class, (context, message) -> active(data.addElement(message)))
  21. .onMessage(
  22. Flush.class,
  23. (context, message) -> {
  24. data.getTarget().tell(new Batch(data.queue));
  25. return idle(data.copy(new ArrayList<>()));
  26. })
  27. .onMessage(
  28. Timeout.class,
  29. (context, message) -> {
  30. data.getTarget().tell(new Batch(data.queue));
  31. return idle(data.copy(new ArrayList<>()));
  32. })
  33. .build();
  34. });
  35. }

要设置状态超时,请使用Behaviors.withTimersstartSingleTimer

以前在onTransition块中所做的任何副作用(side effects)都直接进入行为。


英文原文链接Behaviors as Finite state machines.