适当的 PersistentEntityRef

Lagom PersistentEntityRef

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

我已阅读以下说明:

Every PersistentEntity has a fixed identifier (primary key) that can be used to fetch the current state and at any time only one instance (as a “singleton”) is kept in memory.

有道理。

然后就是下面这个创建客户的例子:

@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));
   };
}

这让我很困惑:

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

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

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

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

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

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

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

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

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