【问题标题】:Can I use akka persistence when the actor state is only increase in size?当演员状态只是增加大小时,我可以使用 akka 持久性吗?
【发布时间】:2015-11-01 04:45:52
【问题描述】:

我正在使用 akka 持久性尝试实现一个服务,其中我的状态可能非常大(假设它不适合 RAM)一些实体列表。假设用户希望所有实体的所有历史记录都可用。我可以在 akka 持久性中做到这一点吗?

现在我的演员状态是这样的。

case class System(var processes: Map[Long, Process] = Map()) {

  def updated(event: Event): System = event match {
    case ProcessDetectedEvent(time, activitySets, id, processType) =>
      val process = Process(activitySets.coordinates, time, activitySets.channels, id, processType, false)
      copy(processes = processes + (id -> process))

    case ProcessMovedEvent(id, activitySets, time) =>
      val process = Process(activitySets.coordinates, time, activitySets.channels, id, processes(id).processType, false)
      copy(processes = processes + (id -> process))

    case ProcessClosedEvent(time, id) =>
      val currentProcess = processes(id)
      val process = Process(currentProcess.coordinates, time, currentProcess.channels, id, currentProcess.processType, true)
      copy(processes = processes + (id -> process))
    case _ => this
  }

}

正如您所见,进程的映射存储在内存中,因此如果进程数量很大,应用程序可能会耗尽内存。

【问题讨论】:

  • 过去几个月我一直在研究 ES,我认为你真的应该尝试拥有一个 ProcessRootAggregate,它会产生其他子 ProcessAggregates,而不是将所有进程存储在一个 PersistentActor 中。您的 ProcessRootAggregate 应该向它的所有子角色 (ProcessAggregates) 发送消息以执行一些计算。看看这个模板github.com/ScalaConsultants/akka-persistence-eventsourcing

标签: event-sourcing akka-persistence


【解决方案1】:

Akka 持久性被有状态的 Actor 用于在 Actor 启动、JVM 崩溃后重新启动或由主管重新启动或在集群中迁移时恢复其内部状态。在这种情况下,从长远来看,应用程序/JVM 可能会因 OutOfMemory 异常而崩溃。当这个actor重新启动时,持久性恢复机制会在map中重新创建所有Process的信息。但是总内存又会很高,应用程序在运行时可能会再次崩溃。因此,在这种情况下,持久化将无助于避免应用程序崩溃,除非您仅持久化部分进程列表以减少内存。

所以首先你需要想办法解决这个内存异常。也许您可以尝试以下选项。

  1. 在 OutOfMemory 异常后 JVM 重新启动期间尝试增加 JVM 堆大小。
  2. 在恢复状态时,仅重播选定的消息列表,这样使用的总内存会很低但状态不完整。

如果恢复过程中要重放的消息列表太大,可以使用快照来减少状态恢复时间。

【讨论】:

  • 我认为我的问题不够清楚。我不能有不完整的状态,这就是问题的重点。我已经找到了一种解决方案 - 在 actor 的内存中没有状态,而是直接将其写入某个用于查询状态的数据库。那基本上是CQRS。我不明白的是,为什么基本上所有关于 akka 持久性的教程和示例都没有任何相关信息。就像他们在教程中构建一些客户/订单系统一样,但他们从不从内存中删除它们。这令人困惑。
  • 答案仍然适用于 CQRS,即这些模式涵盖了非常适合理想世界的场景(尽管我们都知道现实世界略有不同)。
【解决方案2】:

也许您想考虑是否有有意义的方法将您的数据集划分为具有合理界限的范围。然后你可以用一个持久的actor来表示每个范围,如果你需要跨越整个商店的信息,你就必须有某种协调器来管理范围和迭代它们。但取决于这开始变得多么复杂,在某些时候,我不得不怀疑你是否会重新发明 map-reduce 或 Spark。

【讨论】:

    【解决方案3】:

    我认为您可能正在寻找(至少这是另一种选择)是快照。

    在使用事件溯源和事件回复时,通常建议的方法是每隔一段时间使用一次快照。

    因此,当您返回事件时,您会返回一个快照,然后是该快照后发生的事件。这意味着您从事件存储中流式传输的对象更少(内存更少),需要处理和应用的东西更少(更快)......但这确实带来了它自己的权衡......我不会在这里讨论。

    这再次仅涵盖最常见的情况。如果您的事件处理发生变化,那么您可能需要重新构建您的事件......尽管这会引发一些关于您如何构建系统的相当严肃和有趣的问题。

    我没有仔细看,但 akka 可能内置了快照的概念。如果不是这样,那么当你开始走上所有那些以理想方法少有人走但现实世界向你抛出的道路时,前面的道路就会出现学习曲线和大量的反复试验。

    【讨论】:

    • 我很抱歉,但不仅这个答案没有回答我的问题,即如果演员状态只是增加大小该怎么办。快照将加载到内存中。如果它大于内存可以容纳,那么它就行不通了。
    猜你喜欢
    • 2023-03-03
    • 2019-11-24
    • 2019-04-23
    • 2017-11-30
    • 1970-01-01
    • 2014-07-10
    • 1970-01-01
    • 2010-12-13
    • 2023-03-28
    相关资源
    最近更新 更多