我最近一直在调查演员模型和阿克卡。作为一个OOP程序员,我试图坚持松散耦合和“告诉不要问”。我还使用消息传递和事件在有限的上下文之间进行通信。所以,演员的模式对我来说很有意义。
据我理解,使用Actors的主要动机是在并发环境中管理状态。我日常工作的大部分内容是编写后端业务应用程序,典型的模式是从DB加载实体、检查不变量、处理命令、持久化、引发事件。如果我有一个高负载和许多并发更改,我可能选择使用事件源,而不是更新数据库。因此,我的并发挑战确实由我的数据库层来处理。
所以,我的问题是,Actor模型主要是一个优势,您可以处理快速变化的、并发的和内存中的状态管理,例如在线多人游戏。
发布于 2022-11-22 16:35:21
演员模型确实有助于管理内存状态,并通过并发性方面显着地简化了模式/技术,如数字双胞胎和内存映像。
还有其他好处:通信和延迟在模型中是显式的,以便于分发是一个主要的好处。应用程序与纯参与者模型的距离越近,就越有能力将多个副本作为一个应用程序呈现,从而带来相应的弹性好处。还有一个处理故障的故事(与错误处理不同:它们非常不同),它在构建可靠的系统方面有着悠久的历史。
但是对于你所说的“后端商业应用程序”,(从有限上下文的讨论等等)。看起来您至少遵循了一些DDD模式,能够将状态保存在内存中是一个很大的胜利。
考虑一个DDD聚合,注意到该聚合中所有对实体的访问都是通过其根访问的,并注意到聚合形成了一致性边界。这两者都很符合参与者模型:模型的并发性保证只有在参与者的内部状态只能通过参与者访问,并且一致性边界通过将聚合上的操作编码为给参与者的消息/命令并依赖于一次性消息处理时才能得到满足。因此,将聚合的每个实例建模为一个参与者是合理的。
为了简单起见,我将假设(在不失去通用性的情况下),但单聚合-多实体的情况引入了一些复杂性,围绕着在同一时间保持一条消息外部的错觉: Akka术语的广义方法是将聚合映射到一个切分实体(存在一个术语错配.)以及聚合中的实体(可能是该实体的子角色),即聚合包含单个实体。
有一个请求,我们解析请求的聚合根,我们有一个Map<AggregateRoot, ActorRef<AggregateCommand>> (我在这里使用类型化API,我们可以假设映射是并发的)跟踪聚合的活动内存实例。如果内存中已经有一个实例,则将请求转换为AggregateCommand并将其发送给参与者(我们可能会使用ask模式,以便可以返回一个不同的响应,以判断命令是否被接受或拒绝);如果内存中还没有实例,我们将生成一个实例并将其保存到地图中,然后再按前面的方式继续。
作为旧逻辑的直接转换,我们的聚合参与者将对来自DB的实体的每个命令加载状态进行检查,验证某些不变量是否保持,处理命令以更新内存中的状态,保持更新的状态并引发事件(可能使用某种事务性发件箱模式,否则即使状态被更新.)也不会实际引发任何事件。
目前还不清楚您对并发命令执行采取了什么策略(目前我将忽略事件源)。也许您能够在数据库模式中编码所有域约束(让数据库负责并发性),这是很棒的(我们都应该很幸运!),尽管将域规则分散到代码和DB模式之间会引入一个潜在的冲突点,并意味着对这些规则的某些类别的更改需要模式迁移和所有这些痛苦。或者,一些乐观的并发控制可能正在起作用:如果并发更新已经完成,我们可能会从DB和以后重新加载。
由于参与者和并发Map结合起来防止并发修改(我们基本上引入了悲观的并发控制),所以我们可以在不并发的假设下坚持,从而不支付并发税。类似地,我们可以避免从DB加载实体,并验证不变量是否包含每条消息:只要在任何时候对聚合实例最多有一个参与者,并且只有这样的一个参与者更新DB (命令处理维护不变量,这是您真正希望的情况),当参与者启动并在- memory状态中从命令到命令使用相同的状态时,将实体加载到内存中就足够了。基本上,您将内存中的表示声明为DB表示,这只是为了持久性,并且您还为域逻辑和约束创建了一个单一的真理源。作为一个附带的好处,您已经在并发下消除了大部分DB负载(读取、DB并发解析、在并发下重试)。
这听起来很复杂,但这种模式有一些预先存在的实现:并发Map是由Akka集群分块提供的(注意,一个节点的集群是完全可以接受的),我刚刚基本上描述了Akka Persistence的DurableStateBehavior API。
注意,我们已经戏剧性地改变了DB的工作负载:它已经从基本上1读:1写变成了1读:n写(n >= 1)。您现在可以调优数据库的写入功能,特别是当您正在执行CQRS时(如果您正在为其他有界上下文发布域事件,则至少已经做了一些CQRS )。转移到事件源(例如Akka Persistence的EventSourcedBehavior),如果使用细粒度的事件,则会降低写入的成本,而代价是读取(因为事件只涉及实体的一部分,并且在读取快照时可能很小,然后许多事件几乎肯定比一次读取整个状态更昂贵),这种优化放大了我们新的写重工作负载的好处。
(免责声明:我受雇于Akka的主要开发人员Lightbend,但这也是我在受雇之前就会给出的相同答案)
https://stackoverflow.com/questions/74530132
复制相似问题