返回介绍

7.9 Actor 的内置状态转换

发布于 2024-08-21 22:20:21 字数 3533 浏览 0 评论 0 收藏 0

在很多场景下,Actor的业务逻辑可能比较复杂,Actor可能需要根据不同的状态对同一条消息作出不同的处理。Akka已经为我们考虑到了这一点,一个Actor内部消息处理函数可以拥有多个不同的状态,在特定的状态下,可以对同一消息进行不同的处理,状态之间也可以任意切换。

现在让我们模拟一个婴儿Actor,假设婴儿会拥有两种不同的状态,开心或者生气。当你带他玩的时候,他总是会表现出开心状态,当你让他睡觉时,他就会非常生气,小孩子总是拥有用不完的精力,入睡困难可能是一种通病吧!

在我们这个简单的场景模拟中,我们会给这个婴儿Actor发送睡觉和玩两种指令。如果婴儿正在生气,你还让他睡觉,他就会说“我已经生气了”,如果你让他去玩,他就会变得开心起来。同样,如果他正玩得高兴,你让他继续玩,他就会说“我已经很愉快了”,如果让他睡觉,他就马上变得很生气。

下面的这个BabyActor就模拟了上述场景:

01 public class BabyActor extends UntypedActor {
02   private final LoggingAdapter log = Logging.getLogger(getContext().system(), this);
03   public static enum Msg {
04     SLEEP, PLAY, CLOSE;
05   }
06
07   Procedure<Object> angry = new Procedure<Object>() {
08     @Override
09     public void apply(Object message) {
10       System.out.println("angryApply:"+message);
11       if (message == Msg.SLEEP) {
12         getSender().tell("I am already angry", getSelf());
13         System.out.println("I am already angry");
14       } else if (message == Msg.PLAY) {
15         System.out.println("I like playing");
16         getContext().become(happy);
17       }
18     }
19   };
20
21   Procedure<Object> happy = new Procedure<Object>() {
22     @Override
23     public void apply(Object message) {
24       System.out.println("happyApply:"+message);
25       if (message == Msg.PLAY) {
26         getSender().tell("I am already happy :-)", getSelf());
27         System.out.println("I am already happy :-)");
28       } else if (message == Msg.SLEEP) {
29         System.out.println("I don't want to sleep");
30         getContext().become(angry);
31       }
32     }
33   };
34
35   @Override
36   public void onReceive(Object msg) {
37     System.out.println("onReceive:"+msg);
38     if (msg == Msg.SLEEP) {
39       getContext().become(angry);
40     } else if (msg == Msg.PLAY) {
41       getContext().become(happy);
42     } else {
43       unhandled(msg);
44     }
45   }
46 }

上述代码中,使用了become()方法用于切换Actor的状态(第39、41行)。方法become()接收一个Procedure参数。Procedure在这里可以表示一种Actor的状态,同时,更重要的是它封装了在这种状态下的消息处理逻辑。

在这个BabyActor中,定义了两种Procedure,一是angry生气(第7行),另一个是happy开心(第21行)。

在初始状态下,BabyActor既没有生气也不开心。因此angry处理函数和happy处理函数都不会工作。当BabyActor接收到消息时,系统会调用onReceive()方法来处理这个消息。

令人吃惊的魔法就在这个onReceive()函数中。当onReceive()处理SLEEP消息时,它会切换当前Actor的状态为angry(第39行)。如果是PLAY消息,则切换状态为happy。

一旦完成状态切换,当后续有新的消息送达时,就不会再由onReceive()函数处理了。由于angry和happy本身就是消息处理函数。因此,后续的消息就直接交由当前状态处理(angry或者happy),从而很好地封装了Actor的多个不同处理逻辑。

下面的代码向我们的婴儿Actor发送了几条PLAY和SLEEP的消息:

1 ActorSystem system = ActorSystem.create("become", ConfigFactory.load("samplehello.conf"));
2 ActorRef child = system.actorOf(Props.create(BabyActor.class), "baby");
3 system.actorOf(Props.create(WatchActor.class, child), "watcher");
4 child.tell(BabyActor.Msg.PLAY, ActorRef.noSender());
5 child.tell(BabyActor.Msg.SLEEP, ActorRef.noSender());
6 child.tell(BabyActor.Msg.PLAY, ActorRef.noSender());
7 child.tell(BabyActor.Msg.PLAY, ActorRef.noSender());
8
9 child.tell(PoisonPill.getInstance(), ActorRef.noSender());

其输出如下(进行过适量裁剪):

onReceive:PLAY
happyApply:SLEEP
I don't want to sleep
angryApply:PLAY
I like playing
happyApply:PLAY
I am already happy :-)
[INFO][akka://become/user/watcher] akka://become/user/baby has terminated, shutting down
system

可以看到,当第一个PLAY消息到来时,是由onReceive()函数进行处理的,在onReceive()中,将Actor切换为happy状态。因此,当SLEEP消息达到时,由happy.apply()函数处理,接着Actor切换为angry状态。当PLAY消息再次到达时,由angry.apply()函数处理。由此可见,Akka为Actor提供了灵活的状态切换机制,处于不同状态的Actor可以绑定不同的消息处理函数进行消息处理,这对构造结构化应用有着重要的帮助。

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文