【问题标题】:Lagom PersistentEntityRefLagom PersistentEntityRef
【发布时间】:2018-05-21 08:23:58
【问题描述】:

我正在研究 Lagom,并试图了解持久实体的工作原理。

我已阅读以下说明:

每个 PersistentEntity 都有一个固定的标识符(主键),可以 用于获取当前状态,并且在任何时候只有一个实例 (作为“单例”)保存在内存中。

有道理。

那么有下面的例子来创建一个客户:

@Override
public ServiceCall<CreateCustomerMessage, Done> createCustomer() {
   return request -> {
       log.info("===> Create or update customer {}", request.toString());
       PersistentEntityRef<CustomerCommand> ref = persistentEntityRegistry.refFor(CustomerEntity.class, request.userEmail);
       return ref.ask(new CustomerCommand.AddCustomer(request.firstName, request.lastName, request.birthDate, request.comment));
   };
}

这让我很困惑:

  • 这是否意味着persistentEntityRegistry 包含多个单例persistentEntities? persistentEntityRegistry 究竟是如何被填充的,里面有什么?假设我们创建了 10k 个用户,注册表是否包含 10k 个持久实体,还是只有 1 个?
  • 在这种情况下,我们要创建一个新客户。因此,当我们使用persistentEntityRegistry.refFor(CustomerEntity.class, request.userEmail); 请求持久实体时,这不应该从注册表返回任何内容,因为客户还不存在(?)。

你能解释一下它是如何工作的吗? 文档很好,但我的理解中有一些我无法填补的漏洞。

【问题讨论】:

    标签: lagom


    【解决方案1】:

    好问题。我不确定您对此处未提及的与持久实体相关的概念了解多少,所以我将从头开始。

    在进行事件溯源时,通常,对于给定的实体(例如,单个客户),您需要一个编写器。这是因为通常读取然后写入事件日志不是在单个事务中完成的,因此您读取一些事件以加载您的状态,验证传入命令,然后发出一个或多个新事件以进行持久化。如果两个操作同时进入同一个实体,那么它们都将使用相同的状态进行验证——不考虑另一个在执行之前可能进入的状态变化。因此,事件溯源需要单一写入器原则,一次只能处理一个操作,因此只有一个写入器。

    在 Lagom 中,这是使用演员实现的。每个实体(即客户的每个实例)都由参与者加载和管理。参与者有一个邮箱(即一个队列),命令被放置在其中,并且它按顺序一次处理一个。对于每个实体,都有一个单独的参与者管理它(因此,每个客户一个参与者,许多客户的多个参与者)。由于单一作者原则,这一点非常重要。

    但是,这样的系统如何扩展?如果您有多个节点会发生什么,那么您是否有每个实体的多个实例?不会。Lagom 使用 Akka 集群和 Akka 集群分片来将您的实体分片到多个节点,确保在所有部署的节点中,每个实体只有一个。因此,当命令进入一个节点时,实体可能存在于同一个节点上,在这种情况下,它会直接发送到本地参与者进行处理,或者它可能存在于不同的节点上,在这种情况下它被序列化,发送到它所在的节点,并在那里处理,响应被序列化并发回。

    这是PersistentEntityRef的原因之一,由于位置透明(你不知道实体住在哪里),你不能直接抓住实体,你只能参考它。演员使用相同的术语,您拥有执行该行为的实际 Actor,并使用 ActorRef 与之通信。

    现在,从逻辑上讲,当您获得客户的参考时,根据您的系统的域模型,该客户尚不存在(例如,他们尚未注册),他们不存在。但是,他们的持久实体可以而且必须存在。 Lagom 中实际上没有持久实体不存在的概念,您始终可以实例化任何 id 的持久实体,它将加载。只是该实体可能还没有事件,在这种情况下,当它加载时,它只会有它的initialState,没有应用任何事件。对于客户,客户的状态可能是Optional&lt;Customer&gt;。因此,在为客户发出任何事件之前首次创建实体时,状态将为Optional.empty()。为客户发出的第一个事件将是 CustomerRegistered 事件,这将导致状态更改为 Optional.of(someCustomer)

    要理解为什么在逻辑上必须如此,您不希望同一个客户能够注册两次,对吧?您希望确保每个客户只有一个 CustomerRegistered 事件。为此,您需要为处于未注册状态的客户提供一个状态,以在他们注册之前验证他们尚未注册。

    所以,为了明确第一个问题的答案,如果您有 10K 用户,那么将有 10K 个持久实体实例,每个用户一个。不过,这只是逻辑上的(物理上数据库中会有 10K 不同用户的事件)。在内存中,实际加载的实体将取决于哪些实体处于活动状态,当一个实体在默认情况下持续 2 分钟而没有收到命令时,Lagom 将钝化该实体,这意味着它会从内存中删除它,所以下一次一个命令进来,因为它必须通过从数据库加载它的事件来加载。如果您不希望将所有数据保存在内存中,这有助于确保您不会耗尽内存。

    【讨论】:

    • 这澄清了谢谢。也许还有一个额外的问题:您可能不想一直在内存中维护 1k 个客户。实体何时从内存中删除?我们应该配置它还是自动发生?顺便说一句,解释很清楚。
    • 这在我的最后一段中提到了——它们在钝化时会从内存中删除,默认情况下是在 2 分钟不活动之后。相关文档在这里:lagomframework.com/documentation/1.4.x/java/…,配置选项是 lagom.persistence.passivate-after-idle-timeout
    猜你喜欢
    • 2023-03-07
    • 1970-01-01
    • 2018-05-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-08-14
    • 1970-01-01
    相关资源
    最近更新 更多