如何在事件驱动的体系结构中处理初始状态?


33

事件驱动的体系结构中,每个组件仅在事件通过系统发送时起作用。

假设有一辆带有刹车踏板和刹车灯的假想汽车。

  • 刹车灯接收到刹车启动事件时亮起,并在接收到刹车关闭事件时熄灭
  • 制动踏板发送brake_on当它被按下事件和brake_off当它被释放事件。

这是一切都很好,直到你有其中汽车已开启,制动踏板的情况已经按下。由于刹车灯从未收到过刹车事件,因此它将保持熄灭状态-显然是不希望的情况。默认情况下,打开刹车灯只会扭转这种情况。

如何解决此“初始状态问题”?

编辑:谢谢您的所有答复。我的问题不是关于一辆真正的汽车。在汽车中,他们通过不断发送状态来解决此问题-因此在该域中没有启动问题。在我的软件领域,该解决方案将使用许多不必要的CPU周期。

编辑2:除了@gbjbaanb的答案外,我还将使用以下系统:

  • 假设的制动踏板在初始化后会发送一个事件及其状态,并且
  • 假设的制动灯在初始化后发送一个事件,要求从制动踏板发出状态事件。

使用此解决方案,组件之间没有依赖关系,没有竞争条件,没有消息队列过时,也没有“主”组件。


2
首先想到的是生成一个“合成”事件(称为initialize),其中包含所需的传感器数据。
msw 2015年

踏板不应该发送ake_pedal_on事件,而实际的制动器是否发送brake_on事件?如果刹车不起作用,我不希望我的刹车灯亮起。
bdsl

3
我是否提到过这是一个假设的例子?:-)为了使问题简短明了,将其简化了很多。
Frank Kusters,2015年

Answers:


32

有很多方法可以做到这一点,但是我更喜欢使基于消息的系统保持尽可能的分离。这意味着整个系统无法读取任何组件的状态,也无法读取任何其他组件的状态(那样就意味着依赖关系的意大利面条式联系)。

因此,尽管运行中的系统会自行维护,但我们需要一种方法来告诉每个组件自行启动,并且组件注册中已经包含了此类内容,即,启动时核心系统必须告知每个组件现在已注册(或要求每个组件返回其详细信息以便可以注册)。在此阶段,组件可以执行其启动任务,并且可以像在正常操作中一样发送消息。

因此,当点火开关启动时,制动踏板会收到来自汽车管理部门的注册/检查消息,它不仅会返回“我在这里正在工作”消息,而且还会检查自己的状态并发送该状态的消息(例如踏板踩下消息)。

然后问题就成为启动依赖项之一,好像刹车灯尚未注册,那么它将不会收到消息,但是通过将所有这些消息排队直到核心系统完成启动,注册和检查例程,可以轻松解决此问题。 。

最大的好处是,除了您必须编写外,不需要特殊的代码来处理初始化(好吧,如果有关制动踏板事件的消息发送在制动踏板处理器中,您也必须在初始化中调用它) ,但这通常不是问题,除非您已经编写了与处理程序逻辑紧密相关的代码),并且组件之间没有交互,除了它们之间已经正常发送的组件之外。因此,消息传递体系结构非常好!


1
我喜欢您的回答,因为它使所有组件保持分离-这是选择此体系结构的最重要原因。但是,当前没有真正的“主”组件可以确定系统处于“初始化”状态-一切都开始运行。结果是我的问题出现了。一旦主机确定系统正在运行,它就可以向所有组件发送“系统初始化”事件,此后每个组件都开始广播其状态。问题解决了。谢谢!(现在我剩下的问题是如何确定系统是否已初始化...)
Frank Kusters 2015年

如何让状态更新调度程序跟踪从每个对象收到的最新更新,并且每当收到新的订阅请求时,它是否将向新订阅者发送从注册事件源接收到的最新更新?
超级猫

在这种情况下,您还必须跟踪事件何时终止。并非所有事件都可以永久保存可能注册的任何新组件。
Frank Kusters,2015年

@spaceknarf好,在“一切都开始运行”的情况下,您无法在组件中建立依赖关系,因此踏板在灯光后启动,您只需要按此顺序启动它们,尽管我想有些事情会使它们开始运行,所以运行它们以“正确”的顺序排列(例如,systemd之前的linux启动init脚本,其中首先启动的服务称为1.xxx,第二个称为2.xxx,依此类推)。
gbjbaanb 2015年

具有这样顺序的脚本很脆弱。它包含很多隐式依赖。相反,我在想,如果您有一个“主”组件,该组件具有应运行的静态配置的组件列表(如@Lie Ryan所述),则一旦所有这些组件都被加载,它便可以广播“就绪”事件。响应于此,所有组件都广播其初始状态。
Frank Kusters 2015年

4

您可以具有一个初始化事件,该事件在加载/启动时适当地设置状态。对于不包含多个硬件部件的简单系统或程序,这可能是理想的,但是对于具有多个物理组件的更复杂的系统,因为您承担着根本不初始化的风险-如果在通信过程中丢失或丢失了“制动”事件系统(例如,基于CAN的系统)时,您可能会无意中将系统向后设置,就像在踩下制动器的情况下启动系统一样。您可能拥有的控制器越多,例如在汽车上,错过某件事的可能性就越大。

为了解决这个问题,您可以使“制动”逻辑反复发出“制动”事件。也许每1/100秒左右。包含大脑的代码可以侦听这些事件,并在接收到这些事件时触发“刹车”。在1/10秒内未收到“ brake on”信号时,它会触发内部“ brake_off”事件。

不同的事件将具有非常不同的时序要求。在一辆汽车,你的刹车灯需要比说快得多支票燃料光(其中multisecond延迟可能是可以接受的)或其他不太重要的系统。

物理系统的复杂性将决定使用哪种方法更合适。假设您的示例是车辆,则可能需要与后者类似的东西。

无论哪种方式,对于物理系统,您都不希望依赖于正确接收/处理的单个事件。因此,联网系统上连接的微控制器通常会出现“我还活着”的超时。


在物理系统中,您将使用电线并使用二进制逻辑:HIGH踩下制动器,LOW踩下制动器
棘轮怪胎2015年

@ratchetfreak这类事情有很多可能性。也许可以解决这个问题。还有许多其他系统事件不能简单地处理。
enderland'2

1

在这种情况下,我不会将制动器建模为简单的开/关。相反,我会发送“制动压力”事件。例如,压力0表示关闭,压力100完全按下。系统(节点)将根据需要不断地(以一定间隔)将断裂压力事件发送到控制器。

当系统启动时,它将开始接收压力事件,直到将其关闭。


1

如果通过状态传递状态信息的唯一方法是麻烦,那么您就麻烦了。相反,您需要同时具备以下两个条件:

  1. 查询制动踏板的当前状态,以及
  2. 注册来自制动踏板的“状态更改”事件。

可以将制动灯看作是制动踏板的观察者。换句话说,刹车踏板对刹车灯一无所知,没有刹车灯就可以操作。(这意味着对制动踏板主动向制动灯发送“初始状态”事件的任何想法都是错误的。)

在系统实例化时,制动灯会向制动踏板对准,以接收制动通知,并读取制动踏板的当前状态并自行打开或关闭。

然后,可以通过以下三种方式之一实施制动通知:

  1. 作为无参数的“制动踏板状态已更改”事件
  2. 作为一对“现在踩下制动踏板”和“现在释放制动踏板”事件
  3. 参数为“按下”或“释放”的“新踏板状态”事件。

我更喜欢第一种方法,这意味着在收到通知后,刹车灯将简单地执行其已经知道的操作:读取刹车踏板的当前状态并自行打开或关闭。


0

在一个事件驱动的系统(我目前正在使用并喜欢)中,我发现使事物尽可能地分离是很重要的。因此,考虑到这个想法,让我们深入研究。

拥有一些默认状态很重要。您的刹车灯将采用默认状态“关闭”,而您的刹车踏板将采用默认状态“向上”。此后的任何更改将是一个事件。

现在解决您的问题。想象一下,您的制动踏板被初始化并踩下了,事件触发了,但是还没有刹车灯可以接收事件。我发现初始化任何逻辑之前,将对象的创建(事件监听器将在其中初始化)分离为一个单独的步骤是最容易的。正如您所描述的那样,这将防止出现任何竞赛情况。

我还发现对于实际上是同一件事使用两个不同的事件很尴尬。brake_offbrake_on可以简化为e_brake参数bool on。您可以通过添加支持数据来简化事件。


0

您需要的是广播事件和消息收件箱。广播是发布给未指定数目的侦听器的消息。组件可以订阅广播事件,因此它只接收它感兴趣的事件。这提供了去耦,因为发送者不需要知道接收者是谁。在组件安装期间(而不是在初始化时)需要静态配置订阅表。收件箱是消息路由器的一部分,当目标组件脱机时,它充当缓冲区来保存消息。

使用发票会带来一个问题,就是收件箱的大小。您不希望系统为不再在线的组件保留越来越多的消息。这对于具有严格内存限制的嵌入式系统尤其重要。为了克服收件箱大小限制,所有广播的邮件都需要遵循一些规则。规则是:

  1. 每个广播事件都需要一个名称
  2. 在任何给定时间,广播事件的发送者只能进行一个具有指定名称的活动广播
  3. 事件引起的影响必须是幂等的

广播名称需要在组件安装期间声明。如果组件在接收者处理前一个广播之前发送了具有相同名称的第二个广播,则新广播将覆盖前一个广播。现在,您可以有一个静态的收件箱大小限制,可以保证它永远不会超过特定大小,并且可以根据订阅表进行预先计算。

最后,您还需要一个广播存档。广播档案是一个表,其中包含每个广播名称中的最后一个事件。刚安装的新组件的收件箱中将预装有广播存档中的消息。像消息收件箱一样,广播存档也可以具有静态大小。

此外,要处理消息路由器本身处于脱机状态的情况,您还需要消息发件箱。消息发件箱是临时保存传出消息的组件的一部分。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.