Akka 有限状态机实例

Posted

技术标签:

【中文标题】Akka 有限状态机实例【英文标题】:Akka finite state machine instances 【发布时间】:2018-11-13 20:16:30 【问题描述】:

我正在尝试将 Akka 的有限状态机框架用于我的用例。我正在开发一个系统来处理经过各种状态的请求。

这里的请求是需要连同它所依赖的应用一起部署的应用名称:

Request for application A -> A is in a QUEUED state
Discover A's dependency B -> B is in a QUEUED state
B is being processed -> B is in a PROCESSING STATE
A is being processed -> A is in a PROCESSING STATE
B is processed -> B is in a DONE state
A is processed -> A is in a DONE state

为此,我在发现时初始化了一个有限状态机。所以A的FSM是在请求进来时创建的,B的FSM是在B被其中一个actor发现时初始化的。

我是否初始化并将 FSM 实例传递给所有参与者,同时tell FSM 关于正在对数据执行的操作以使状态机进入正确的状态?

下面是状态机的相关部分:

when(QUEUED, matchEvent(requestAccepted.class, MyApp.class,
    (requestAccepted, service) -> 
    goTo(PROCESSING).replying(PROCESSING)));

when(PROCESSING, matchEvent(completed.class, MyApp.class,
    (completed, service) -> goTo(DONE).replying(DONE)));

// During transitions, save states in the database.
onTransition(matchState(PROCESSING, DONE, () -> 
  nextStateData().setServiceStatus(DONE);
  databaseWriter.tell(nextStateData(), getSelf());

以下是处理请求的参与者之一的示例:

ProcessingActor extends AbstractActor 

    @Override
      public void onReceive(Object message) throws Throwable 
        if (message instanceof processApplication) 
         // process the app
         // Initialize FSM for the Application
         FSM myFSM = Props.create(MYFSM.class);
         myFSM.tell( new completed(processApplication.app)
    

这是初始化状态机并使用它的正确方法吗?还是应该在ProcessingActor 的构造函数中进行初始化?但在这种情况下,每个应用程序(数据)不会有一个状态机。

【问题讨论】:

我认为这个问题缺少答案,因为它非常不清楚。我重读了 3 次,但仍然不确定您要达到什么目标以及如何实现。我认为由于您似乎以至少两种不同的含义使用术语“应用程序”,否则我完全不明白您的句子我正在处理 应用程序 处理应用程序 ...”的意思。请尝试在问题中添加更多详细信息,然后有人可能会帮助您。 我认为这个问题在当前状态下无法回答。它过于基于意见和模糊。如果您的解决方案有效,那么它就有效。谁来说什么是“正确的方式”。 它现在可以工作了,但我想知道这种设计是否会持续下去。 一般而言,AKKA 解决方案不会编写一次。 API 往往会随着时间而改变,预计需要为新的 akka 版本重写它。如果你使用 akka 2.5.x,你应该使用 receivebuilders。而不是 onReceive。 FSM 中还有一些更专业的类,比如AbstractFSMWithStash,你可以使用它们来代替一般的AbstractActor 【参考方案1】:

TL;DR;虽然 OP 中对状态和转换的描述相当模糊,但可以解决所选实现的状态、其含义和替代方案的含义。

手头的实现可以算作“每个请求的参与者”方法,而不是更常见的方法,其中状态机参与者缓冲并处理多个请求。下面将进一步介绍。

在目前的形式中,给定的实现是我所知道的唯一 AbstractFSM per request,实际上还有另一个 FSM 下游,它不能再被称为“每个请求”,而且很可能可以避免。大致是这样的:

“每个请求的参与者”最初似乎出现在 this discussion 中,这引起了 this blog 的提升,稍后甚至偶尔声称 a pattern 的头衔。似乎部分原因是复制了 Spray framework 的一个特性,这是 Akka http 的前身。

另一个关于 SO 的讨论出现在 inconclusive result 关于是否更喜欢单个参与者而不是每个请求的问题上,并提出 routing 作为第三种选择,因为路由器还充当负载平衡器,触及 @ 987654328@话题。

不过,与AbstractFSM 及其变体结合使用更常见的方法是使用单个 FSM 演员来缓冲 默认情况下,传入消息使用whenUnhandled 方法来累积它们,无论当前状态是什么(例如Akka in Action,章节"Finite State Machines and Agents" 或Lightbend buncher example)。我无法通过引用来支持该声明,但似乎AbstractFSM 更多地被认为是对处理多个请求的参与者的状态进行建模,而不是对经历多个阶段的请求进行建模。 与 OP 相关的方法意味着,ProcessingActor 本身可以扩展AbstractFSM

public class ProcessingActor extends AbstractFSM<sample.so.State, RequestQueue> 

    startWith(Idle, new RequestQueue());

    when(Idle, /* matchEvent(EventType, DataType, Action) */
            matchEvent(
                    Request.class,
                    RequestQueue.class,
                    (request, queue) -> goTo(Processing).using(queue.push(request)));
    /* more state-matchers */

    whenUnhandled(
            matchEvent(
                    Request.class,
                    RequestQueue.class,
                    (request, qeue) -> stay().using(qeue.push(request)));

    initialize();

对于“请求”部分,数据库写入不再由状态表示,而不是状态进入和退出操作。请注意,所有whenUnhandled 分支都没有出现在图表中,因为它与状态更改无关。

没有过多权衡(模糊)对所选实现的要求,AbstractFSM 似乎是一个相当笨拙的机器,用于将每个请求的状态序列记录到数据库。 Akka 2.4 版本的文档介绍了一个使用 AbstractFsm 的状态机without,并讨论了区分第一个事件然后是状态或反之的可能性。在AbstractFSM-DSL 中,您必须在事件(和数据)之前识别状态。甚至可以有理由反对 akka FSM altogether。

使用become/unbecome 构建FSM 的更轻量级方法,here 是一个很好的演示,AbstractFSM 与成为/不成为的讨论可以在here 找到。更接近业务规则的视觉接近度为前者提供了主要论据。

进入判断使用 AbstractFSM 完成任务的更棘手的领域。我认为,鉴于阅读要求,有些事情可以说是大致可以的。

状态形成一个线性链,因此两个“AbstractFSM-per-request”可以被其他的 per-request 结构替换:

单个参与者链,每个参与者代表一个州

单个参与者,向自己发送事件,上面显示的每个步骤一个事件类型,在每个点向数据库写入器发送消息。也许枚举也足够了。

考虑到这一点,这些版本的吸引力可能会增加:由于给定的实现每个请求使用(至少)一个 FSM,问题就出现了,转换 QUEUED-&gt;PROCESSING 是如何产生的(或discovery processing -&gt; discovery done ),以及QUEUED 与技术水平有关。项目永远不会在队列中,总是在一个独占的参与者中(另一方面,队列状态在single-FSM 方法中会更明显,它实际上使用队列,但是 该状态不适用于参与者,但演员处理的项目)。不明显,哪个external event 应该导致这种转变。 Akka FSM 旨在描述确定性FSM(另请参阅this,出于相反的原因给出相同的论点),但如果此转换不是由外部事件触发,则 FSM 必须变为nondeterministic 并触发它自己的epsilon-transitions ~ 转换不是由任何输入引起的。一个相当复杂的结构,可能是这样实现的:

onTransition(
    matchState(Initial, Queued, () ->                     
        getSelf().tell(new Epsilon(), getSelf());
    ));

【讨论】:

以上是关于Akka 有限状态机实例的主要内容,如果未能解决你的问题,请参考以下文章

(有限)状态机

动画状态机是有限状态机还是行为树

Verilog笔记.3.有限状态机

有限状态机

[UE4]有限状态机动画状态机纯函数

如果在设计有限状态机时,未使用状态的下一个状态设置为初始状态,这种方法称为最小状态( )接近?