akka 如何让参与者在不同的时间接受不同的消息子集?

rqcrx0a6  于 2023-08-05  发布在  其他
关注(0)|答案(1)|浏览(194)

在一些分布式协议中,服务器(参与者)在不同时间在不同角色之间转换,并根据角色接受不同的消息子集。
理想情况下,一个参与者应该始终持有对另一个参与者的引用,该参与者以这样一种方式输入,即它不可能发送错误类型的消息(即,另一个参与者不能接受的消息,因为它处于错误的角色)。
固有的问题是,就像我说的,参与者可以随着时间改变角色,因此它接受的消息子集也会改变;因此,持有对它的引用的所有其他参与者实际上将开始持有错误类型的引用。
具体来说,我正在考虑Raft共识算法,其中(例如)处于follower状态的服务器不会接受AppendEntriesResponse消息,但一旦转换为leader状态,它就能够接受它。如果行为被参数化为包含所有消息(包括AppendEntriesResponse)的Raft类型,那么很容易错误地向关注者发送AppendEntries
akka 对此有什么模式吗
这种模式是否也适用于运行在集群中的参与者?它是否适用于这两种类型的沟通(问,告诉)?
我知道参与者可以显式地忽略一些消息,但我相信这会变得有点麻烦,而且可能容易出错。此外,它并没有消除我上面描述的问题。

o2gm4chl

o2gm4chl1#

不,没有与之完全匹配的现成模式。事实上,我强烈认为这是一个反模式。从本质上讲,您建议将整个集群保持在一个关于单个参与者状态的强一致状态。
考虑一个大型节点集群。Akka使用与您建议的协议类似的协议来维护集群成员列表。该协议可能需要几秒钟(有时甚至更长)才能收敛到新的成员列表。对于集群成员身份,这没什么大不了的,因为在达成共识之前,不会向新成员发送流量。(节点在等待正式从成员资格中删除时可以停止。)对于集群成员资格,在过时状态下运行实际上没有任何成本,并且成员资格列表中不一致的后果很严重。
但是,在您的示例中,如果接收者A想要更改状态,让我们来看看一些挑战。它如何处理队列中已包含的用于旧状态的消息?在宣布状态改变和达成共识之间,它会如何处理收到的消息?相反,当发送方B第一次收到状态改变的通知,但在达成共识之前,它会做什么?它只是暂停吗?还是愿意为新的国家传递信息?如果它没有收到发送到旧状态的消息的回复怎么办?另外,接收者A如何知道持有对它的引用的所有参与者?它如何知道其他人可能不会通过发现或其他方法添加引用?
更不用说您的模式用可用性换取一致性了。当无法达成共识时会发生什么(可能是因为一些节点和/或客户端位于网络分区后面或已经崩溃)?接收器状态是否处于旧的非期望状态?它是否在未确定状态下根本不处理任何消息?将消息保存在stash中,直到状态被解析?(这将加剧潜在地从期望旧状态的客户端接收消息的问题。)
我的观点是,你的建议会有大量的开销。而且即使您愿意接受这种开销,实际上无论如何都要处理“不正确”的消息。除非您愿意接受巨大的延迟和潜在的不可用,等待系统的每个部分完全暂停并确认更改。(即使这样,您似乎也希望处理“不正确”的消息,如果只是为了记录和调试它们的话。)如果您无论如何都要处理“不正确”的消息,并等待耗尽旧消息,那么您最好切换新的、所需的状态,并以某种方式回复“不正确的消息”。
总的来说,在Akka中通常只通过行为更改(或经典Akka中的状态机更改)来处理此模式。新的Behavior只是向发送者返回某种“InvalidState”消息(或者完全丢弃消息,这取决于您的需要)。我已经多次实现了这个模式。从根本上说,Akka是一个异步协议,这意味着处理意外消息实际上是每个参与者的责任,即使在类型化的Akka世界中也是如此。
如果这种模式还不够,你需要更积极主动,你可以:

  • 允许消息发送者“注册状态更改”。这样做有一些挑战,因为您希望确保客户端列表中清除过时的注册者,但概念很简单。我不是很喜欢这个模式,跟踪客户感觉像是一个反模式。但至少它最终是有效一致的:您可能会从没有收到状态更改消息的发件人那里收到一些“无效状态”消息,但这些消息本来已经在传输中,因此无论如何都是不可避免的。
  • 以其他方法发布状态,如分布式数据。从根本上说,这非常类似于发送方在发送实际有效载荷之前发送“preflight check”消息以检查接收方的当前状态的情况(我通常认为这是一个反模式),除了通过使用分布式数据,您可以确保“飞行前检查”消息是在本地处理的,因此在状态变化频繁并且正常消息可能响应缓慢的情况下存在一些潜在的优点。它还为您提供了在更改状态之前等待确认/共识的时间的灵活性。尽管如此,无论如何,您仍然需要考虑可能正在进行的状态消息。

相关问题