【问题标题】:Would storing a rich object as an actor with persistance be a good idea?将丰富的对象存储为具有持久性的演员是一个好主意吗?
【发布时间】:2020-09-29 13:40:41
【问题描述】:

如果您熟悉 Trello,将整个 Trello 看板存储为演员(具有 akka 持久性)是一个很好的用例吗?

一个 trello 板包括:

  • 列表
  • 列表中的任务
  • 每个任务都可以有 cmets 和其他属性

在确定 akka 持久性是否是给定问题集的良好用例时,一般的最佳实践或注意事项是什么?

【问题讨论】:

    标签: akka event-sourcing akka-persistence


    【解决方案1】:

    任何适合事件溯源的环境都适合 Akka Persistence。

    反过来,事件溯源也普遍适用(请注意,您使用的几乎所有数据库都是事件溯源(具有异常频繁的快照、事件日志的截断和旧快照的清除)。

    当您想要显式地建模域中的实体如何随时间变化时,事件溯源非常有效:您实际上是在定义变化的代数。这种变化模型越丰富(即离创建/更新越远),它就越适合事件溯源。这种变化建模反过来又有助于让系统的其他组件只在需要时更新它们的状态。

    Akka Persistence,尤其是与集群分片一起使用时,可以让您处理命令/请求,而不必在每个命令/请求上从数据库中读取(基本上,当带回一个已经持久化的 actor 时,您会从数据库中读取,但是随后的命令/请求(直到演员钝化或死亡)不需要这样的读取)。 Akka 中的父子角色模型也倾向于导致多对一关系的自然编码。

    在 trello 板的例子中,我可能会有

    • 每个董事会都是一个持久的演员,是父母
    • 列表,它们是持久的演员,是每个人的父母
    • 列表项,它们也是持久性参与者

    根据列表项下的数量,它们可能依次具有子持久性actor(用于 cmets 等)。

    领域驱动设计可能值得一读。虽然 DDD 不需要参与者模型(反之亦然),并且它们都不需要事件溯源(反之亦然),但我和其他许多人发现它们可以相互加强。

    【讨论】:

    • 感谢您的概述。您建议的设计的唯一问题是渲染一个板,它需要演员之间的大量沟通,即 1 来获取板,例如 5 来获取 5 个列表,然后如果每个列表有 10 个项目,那就是 50 个调用。每个列表项下的任何内容都意味着 50 x n-things。这真的加起来很快......
    • 您不必为每个参与者进行单独的 REST 调用(参与者间通信的延迟远低于 REST 调用的延迟)。但是,相反,通过一次调用即可获取所有内容,很有可能您最终会请求远远超出您的需要(序列化/反序列化时间很快就会增加)。 child actor 方法的另一个好处是并行性(无论您是直接将 REST 请求路由到孩子还是让董事会进行分散收集,这都是正确的)。
    • 嗨列维!出于好奇,对于您提出的模型,您如何处理一个项目从一个列表到另一个的变化?我不是演员模型的专家,但我认为改变演员的父母是一件复杂的事情。在我看来,您需要执行两个命令才能“从第一个列表中删除”“添加到第二个列表”。将列表项作为板子的直接子项,并将其包含在状态的一部分中的列表(如具有列表 id 的属性)不是更好吗?
    • 通常情况下,actor 的父级不会改变,因此如果属于列表 A 的列表项如果属于列表 B,则严格来说不会是相同的列表项。因此,明智的命令是“从列表中移动” A 到列表 B”将被解释为“建议密封此列表项”、“使用这些属性在列表 B 中创建列表项(其中之一是它曾经是 A 下的该项目)”、“密封此列表带有注释的项目,它被列表 B 中的项目取代”(密封基本上被理解为拒绝进一步写入,但仍可用于读取)。
    • 在一个层面上,它比让董事会由项目组成并且项目引用列表更复杂,但另一方面,像这样利用父子关系的好处是每个属于一个列表的项目是微不足道的。还有一个问题是,相对于“将此项目移动到另一个列表”,您希望多久执行一次“获取此列表中的项目”查询,以及您希望该查询有多一致:是否会执行通常,如果您想要强一致性,则移动操作的复杂性基本上回到了我上面概述的水平。
    【解决方案2】:

    这主要取决于应用想要执行多少写入。

    Akka 持久性是一种在确保数据持久性的同时实现非常高的写入吞吐量的方法,即如果参与者死亡并且内存中的数据丢失,这很好,因为写入日志会持久化到磁盘。

    如果需要数据的持久性,同时不需要非常高的写入吞吐量(想象一下应用程序每秒更新 Trello 板 1 次),那么只需将数据写入外部存储就完全可以了。

    【讨论】:

    • 如果吞吐量不高,那么使用持久性actor总是一个坏主意吗?如果我只是喜欢它的简单性以及保存到磁盘等如何更快地开发呢?
    • @Blankman 你绝对可以,只是注意它带来了额外的复杂性。如果您通常熟悉 Cassandra 和事件溯源,请随意走这条路。只是写入 SQL 数据库非常简单且易于理解
    • 你能给我一些真实世界的例子,说明在哪里使用 akka 持久性是理想的吗?
    【解决方案3】:

    会将整个 Trello 看板存储为演员(使用 akka 持久性)是一个很好的用例

    我会说演员的大小应该与聚合根的大小相匹配。使整个董事会成为聚合根似乎是一个非常糟糕的选择。这意味着该板上的所有操作现在都已序列化,并且不会同时发生。为什么更改卡#1 的描述与将汽车#2 移动到不同的类别相冲突?为什么创建新的棋盘类别会与将卡片 #3 分配给某人发生冲突?

    我的意思是,你可以让整个系统成为一个单一的参与者,你永远不必关心竞争条件,但你也会扼杀你的可扩展性......

    【讨论】:

    • “为什么更改卡#1 的描述与将汽车#2 移动到不同的类别相冲突?”如果命令被序列化,它们不会冲突,它们将一个接一个地执行。在单个“actor”下拥有大数据结构的方法是 redis 在其核心中处理命令(它是单线程)。
    • @rascio 我的意思是,如果它们不是冲突的操作,你为什么要序列化它们?此外,所有数据库都有一种用于所有操作的序列化日志形式(例如预写日志),这并不意味着为业务流程复制该模型是明智的......
    • 好的,现在我明白了你的意思,是的,这是真的。促使我发表评论的是 :) “让整个板子成为聚合根似乎是一个非常糟糕的选择”与它并没有严格的关系。确实每个命令都必须等待,但它为您提供了更好的不变量。如果您需要限制董事会的最大票数,您可以使用单个聚合轻松完成,但在创建新票时将其拆分为每张票的聚合会导致最终的一致性问题。这是一个用你得到的东西来权衡你失去的东西的问题。
    • 我的意思是,没有任何关于使用情况的统计数据,也不知道不变量,我认为不能说 A 比 B 好,B 也不能比 A 好
    • @rascio 我同意,这就是我使用“似乎”而不是“是”的原因:) 大聚合通常表示错误的边界,但这并不意味着它是。至于强一致性与最终一致性:许多规则被人为地强制为强一致性,而最终一致性对业务来说不会成为问题。此外,将数据聚集在一起以执行 1% 的规则似乎也是一个糟糕的选择。对于该特定用例,我更愿意在单个事务中修改许多 AR。你能证明一个大的Board AR 只是为了强制执行最大门票配额吗?
    猜你喜欢
    • 2015-09-30
    • 1970-01-01
    • 2016-12-06
    • 1970-01-01
    • 2018-11-18
    • 2018-09-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多