作为 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;

5. public SetTarget(ActorRef<Batch> ref) {
6. this.ref = ref;
7. }

9. public ActorRef<Batch> getRef() {
10. return ref;
11. }
12. }
13. final class Timeout implements Event {}

15. static final Timeout TIMEOUT = new Timeout();
16. public enum Flush implements Event {
17. FLUSH
18. }
19. static final class Queue implements Event {
20. private final Object obj;

22. public Queue(Object obj) {
23. this.obj = obj;
24. }

26. public Object getObj() {
27. return obj;
28. }
29. }

启动它需要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;

6. public Todo(ActorRef<Batch> target, List<Object> queue) {
7. this.target = target;
8. this.queue = queue;
9. }

11. public ActorRef<Batch> getTarget() {
12. return target;
13. }

15. public List<Object> getQueue() {
16. return queue;
17. }

19. @Override
20. public String toString() {
21. return "Todo{" + "target=" + target + ", queue=" + queue + '}';
22. }

24. public Todo addElement(Object element) {
25. List<Object> nQueue = new LinkedList<>(queue);
26. nQueue.add(element);
27. return new Todo(this.target, nQueue);
28. }

30. public Todo copy(List<Object> queue) {
31. return new Todo(this.target, queue);
32. }

34. public Todo copy(ActorRef<Batch> target) {
35. return new Todo(target, this.queue);
36. }
37. }

每个状态都会变成一种不同的行为。不需要显式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. }

10. private static Behavior<Event> idle(Todo data) {
11. return Behaviors.receive(Event.class)
12. .onMessage(Queue.class, (context, message) -> active(data.addElement(message)))
13. .build();
14. }

16. private static Behavior<Event> active(Todo data) {
17. return Behaviors.withTimers(
18. timers -> {
19. // State timeouts done with withTimers
20. timers.startSingleTimer("Timeout", TIMEOUT, Duration.ofSeconds(1));
21. return Behaviors.receive(Event.class)
22. .onMessage(Queue.class, (context, message) -> active(data.addElement(message)))
23. .onMessage(
24. Flush.class,
25. (context, message) -> {
26. data.getTarget().tell(new Batch(data.queue));
27. return idle(data.copy(new ArrayList<>()));
28. })
29. .onMessage(
30. Timeout.class,
31. (context, message) -> {
32. data.getTarget().tell(new Batch(data.queue));
33. return idle(data.copy(new ArrayList<>()));
34. })
35. .build();
36. });
37. }

要设置状态超时,请使用Behaviors.withTimers和startSingleTimer。

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


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