简体   繁体   English

Akka Java FSM 示例

[英]Akka Java FSM by Example

Please note: I am a Java developer with no working knowledge of Scala (sadly).请注意:我是一名 Java 开发人员,没有 Scala 的工作知识(很遗憾)。 I would ask that any code examples provided in the answer would be using Akka's Java API.我会要求答案中提供的任何代码示例都将使用 Akka 的 Java API。

I am trying to use the Akka FSM API to model the following super-simple state machine.我正在尝试使用 Akka FSM API 对以下超简单状态机进行建模。 In reality, my machine is much more complicated, but the answer to this question will allow me to extrapolate to my actual FSM.实际上,我的机器要复杂得多,但这个问题的答案将使我能够推断出我的实际 FSM。

在此处输入图像描述

And so I have 2 states: Off and On .所以我有两种状态: OffOn You can go fro Off -> On by powering the machine on by calling SomeObject#powerOn(<someArguments>) .您可以通过调用SomeObject#powerOn(<someArguments>)打开机器电源来Off -> On You can go from On -> Off by powering the machine off by calling SomeObject#powerOff(<someArguments>) .您可以通过调用SomeObject#powerOff(<someArguments>)关闭机器电源,从On -> Off开始。

I'm wondering what actors and supporting classes I'll need in order to implement this FSM.我想知道为了实现这个 FSM,我需要哪些演员和支持类。 I believe the actor representing the FSM has to extend AbstractFSM .相信代表 FSM 的演员必须扩展AbstractFSM But what classes represent the 2 states?但是什么类代表这两个州呢? What code exposes and implements the powerOn(...) and powerOff(...) state transitions?哪些代码公开并实现了powerOn(...)powerOff(...)状态转换? A working Java example, or even just Java pseudo-code, would go a long way for me here.一个有效的 Java 示例,甚至只是 Java 伪代码,对我来说会有很长的路要走。

I think we can do a bit better than copypasta from the FSM docs ( http://doc.akka.io/docs/akka/snapshot/java/lambda-fsm.html ).我认为我们可以比 FSM 文档 ( http://doc.akka.io/docs/akka/snapshot/java/lambda-fsm.html ) 中的 copypasta 做得更好。 First, let's explore your use case a bit.首先,让我们稍微探索一下您的用例。

You have two triggers (or events, or signals) -- powerOn and powerOff.您有两个触发器(或事件或信号)——powerOn 和 powerOff。 You would like send these signals to an Actor and have it change state, of which the two meaningful states are On and Off.您希望将这些信号发送到 Actor 并让它改变状态,其中两个有意义的状态是 On 和 Off。

Now, strictly speaking an FSM needs one additional component: an action you wish to take on transition.现在,严格来说,FSM 需要一个额外的组件:您希望在过渡时采取的行动。

FSM:
State (S) x Event (E) -> Action (A), State (S')

Read: "When in state S, if signal E is received, produce action A and advance to state S'"

You don't NEED an action, but an Actor cannot be directly inspected, nor directly modified.不需要一个动作,但是一个 Actor 不能被直接检查,也不能被直接修改。 All mutation and acknowledgement occurs through asynchronous message passing.所有的变异和确认都是通过异步消息传递发生的。

In your example, which provides no action to perform on transition, you basically have a state machine that's a no-op.在您的示例中,它没有提供在转换时执行的操作,您基本上有一个无操作的状态机。 Actions occur, state transitions without side effect and that state is invisible, so a working machine is identical to a broken one.动作发生,状态转换没有副作用,状态是不可见的,所以一台工作的机器和一台坏掉的机器是一样的。 And since this all occurs asynchronously, you don't even know when the broken thing has finished.而且由于这一切都是异步发生的,所以您甚至不知道损坏的东西何时完成。

So allow me to expand your contract a little bit, and include the following actions in your FSM definitions:因此,请允许我稍微扩展您的合同,并在您的 FSM 定义中包含以下操作:

 When in Off, if powerOn is received, advance state to On and respond to the caller with the new state
 When in On, if powerOff is received, advance state to Off and respond to the caller with the new state

Now we might be able to build an FSM that is actually testable.现在我们也许能够构建一个实际上可测试的 FSM。

Let's define a pair of classes for your two signals.让我们为您的两个信号定义一对类。 (the AbstractFSM DSL expects to match on class): (AbstractFSM DSL 期望在类上匹配):

public static class PowerOn {}
public static class PowerOff {}

Let's define a pair of enums for your two states:让我们为您的两个状态定义一对枚举:

 enum LightswitchState { on, off }

Let's define an AbstractFSM Actor ( http://doc.akka.io/japi/akka/2.3.8/akka/actor/AbstractFSM.html ).让我们定义一个 AbstractFSM Actor ( http://doc.akka.io/japi/akka/2.3.8/akka/actor/AbstractFSM.html )。 Extending AbstractFSM allows us to define an actor using a chain of FSM definitions similar to those above rather than defining message behavior directly in an onReceive() method.扩展 AbstractFSM 允许我们使用与上述类似的 FSM 定义链来定义参与者,而不是直接在 onReceive() 方法中定义消息行为。 It provides a nice little DSL for these definitions, and (somewhat bizarrely) expects that the definitions be set up in a static initializer.它为这些定义提供了一个不错的小 DSL,并且(有点奇怪)期望在静态初始化程序中设置这些定义。

A quick detour, though: AbstractFSM has two generics defined which are used to provide compile time type checking.不过,快速绕道:AbstractFSM 定义了两个泛型,用于提供编译时类型检查。

S is the base of State types we wish to use, and D is the base of Data types. S 是我们希望使用的 State 类型的基础,D 是 Data 类型的基础。 If you're building an FSM that will hold and modify data (maybe a power meter for your light switch?), you would build a separate class to hold this data rather than trying to add new members to your subclass of AbstractFSM.如果您正在构建一个将保存和修改数据的 FSM(可能是您的电灯开关的功率计?),您将构建一个单独的类来保存这些数据,而不是尝试向 AbstractFSM 的子类中添加新成员。 Since we have no data, let's define a dummy class just so you can see how it gets passed around:由于我们没有数据,让我们定义一个虚拟类,这样您就可以看到它是如何传递的:

public static class NoDataItsJustALightswitch {}

And so, with this out of the way, we can build our actor class.所以,有了这个,我们可以构建我们的actor类。

public class Lightswitch extends AbstractFSM<LightswitchState, NoDataItsJustALightswitch> {
    {  //static initializer
        startWith(off, new NoDataItsJustALightswitch()); //okay, we're saying that when a new Lightswitch is born, it'll be in the off state and have a new NoDataItsJustALightswitch() object as data

        //our first FSM definition
        when(off,                                //when in off,
            matchEvent(PowerOn.class,            //if we receive a PowerOn message,
                NoDataItsJustALightswitch.class, //and have data of this type,
                (powerOn, noData) ->             //we'll handle it using this function:
                    goTo(on)                     //go to the on state,
                        .replying(on);           //and reply to the sender that we went to the on state
            )
        );

        //our second FSM definition
        when(on, 
            matchEvent(PowerOff.class, 
                NoDataItsJustALightswitch.class, 
                (powerOn, noData) -> {
                    goTo(off)
                        .replying(off);
                    //here you could use multiline functions,
                    //and use the contents of the event (powerOn) or data (noData) to make decisions, alter content of the state, etc.
                }
            )
        );

        initialize(); //boilerplate
    }
}

I'm sure you're wondering: how do I use this?!我敢肯定你想知道:我该如何使用它?! So let's make you a test harness using straight JUnit and the Akka Testkit for java:因此,让我们使用直接的 JUnit 和适用于 java 的 Akka Testkit 为您制作一个测试工具:

public class LightswitchTest { 
    @Test public void testLightswitch() {
        ActorSystem system = ActorSystem.create("lightswitchtest");//should make this static if you're going to test a lot of things, actor systems are a bit expensive
        new JavaTestKit(system) {{ //there's that static initializer again
            ActorRef lightswitch = system.actorOf(Props.create(Lightswitch.class)); //here is our lightswitch. It's an actor ref, a reference to an actor that will be created on 
                                                                                    //our behalf of type Lightswitch. We can't, as mentioned earlier, actually touch the instance 
                                                                                    //of Lightswitch, but we can send messages to it via this reference.

            lightswitch.tell(    //using the reference to our actor, tell it
                new PowerOn(),   //to "Power On," using our message type
                getRef());       //and giving it an actor to call back (in this case, the JavaTestKit itself)

            //because it is asynchronous, the tell will return immediately. Somewhere off in the distance, on another thread, our lightbulb is receiving its message

            expectMsgEquals(LightswitchState.on);   //we block until the lightbulb sends us back a message with its current state ("on.")
                                                     //If our actor is broken, this call will timeout and fail.

            lightswitch.tell(new PowerOff(), getRef());

            expectMsgEquals(LightswitchState.off);   

            system.stop(lightswitch); //switch works, kill the instance, leave the system up for further use
        }};
    }
}

And there you are: an FSM lightswitch.你就在那里:一个 FSM 电灯开关。 Honestly though, an example this trivial doesn't really show the power of FSMs, as a data-free example can be performed as a set of "become/unbecome" behaviors in like half as many LoC with no generics or lambdas.老实说,这个微不足道的例子并没有真正展示 FSM 的力量,因为一个无数据的例子可以作为一组“成为/不成为”的行为,在没有泛型或 lambda 的情况下在一半的 LoC 中执行。 Much more readable IMO.更具可读性的 IMO。

PS consider learning Scala, if only to be able to read other peoples' code! PS考虑学习Scala,如果只是为了能够阅读其他人的代码! The first half of the book Atomic Scala is available free online. Atomic Sc​​ala 一书的前半部分可在线免费获得。

PPS if all you really want is a composable state machine, I maintain Pulleys , a state machine engine based on statecharts in pure java. PPS 如果您真正想要的是一个可组合的状态机,我维护Pulleys ,这是一个基于纯 Java 状态图的状态机引擎。 It's getting on in years (lot of XML and old patterns, no DI integration) but if you really want to decouple the implementation of a state machine from inputs and outputs there may be some inspiration there.它已经持续了好几年(很多 XML 和旧模式,没有 DI 集成),但是如果您真的想将状态机的实现与输入和输出分离,那么可能会有一些灵感。

I know about Actors in Scala.我知道 Scala 中的 Actors。
This Java Start Code may help you, to go ahead:此 Java 开始代码可以帮助您继续:

Yes, extend your SimpleFSM from AbstractFSM .是的,从AbstractFSM扩展您的SimpleFSM
The State is an enum in the AbstractFSM . State 是AbstractFSM中的一个enum
Your <someArguments> can be the Data Part in your AbstractFSM您的<someArguments>可以是AbstractFSM中的Data部分
Your powerOn and powerOff are Actor Messages/Events.您的powerOnpowerOff是 Actor 消息/事件。 And the State switching is in the transitions Part并且状态切换在transitions部分

// states
enum State {
  Off, On
}

enum Uninitialized implements Data {
  Uninitialized
}

public class SimpleFSM extends AbstractFSM<State, Data> {
    {
        // fsm body
        startWith(Off, Uninitialized);

        // transitions
        when(Off,
            matchEvent(... .class ...,
            (... Variable Names ...) ->
              goTo(On).using(...) ); // powerOn(<someArguments>)

        when(On,
            matchEvent(... .class ...,
            (... Variable Names ...) ->
              goTo(Off).using(...) ); // powerOff(<someArguments>)

        initialize();

    }
}

Real working Project see实际工作项目见

Scala and Java 8 with Lambda Template for a Akka AbstractFSM Scala 和 Java 8 以及用于 Akka AbstractFSM 的 Lambda 模板

Well this is a really old question but if you get as a hit from Google, but if you are still interested implementing FSM with Akka, I suggest to look this part of the documentation .好吧,这是一个非常老的问题,但是如果您受到 Google 的欢迎,但如果您仍然对使用 Akka 实现 FSM 感兴趣,我建议您查看文档的这一部分。

If you want to see how a practical model driven state machine implementation, you can check my blog1 , blog2 .如果你想看看一个实用的模型驱动状态机如何实现,可以查看我的blog1blog2

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM