CQRS,事件采购和扩展

时间:2018-06-06 08:29:34

标签: cqrs event-sourcing

很明显,基于这些模式的系统很容易扩展。但我想问你,究竟是怎么回事?关于可伸缩性我几乎没有问题:

  1. 如何扩展聚合?如果我要创建aggregate A的多个实例,如何同步它们?如果其中一个实例处理该命令并创建一个事件,那么该事件应该传播到该agregate的每个实例吗?
  2. 不应该有一些业务逻辑存在要求的agregate实例吗?因此,如果我发出适用于aggregate A (ORDERS)的多个命令并应用于一个特定订单,则将它传递给同一个实例是有意义的。或?
  3. 在本文中:https://initiate.andela.com/event-sourcing-and-cqrs-a-look-at-kafka-e0c1b90d17d8, 他们正在使用Kafka进行分区。因此,用户管理服务 - 聚合被缩放,但仅订阅主题的特定分区,其中包含特定用户的所有事件。

    谢谢!

3 个答案:

答案 0 :(得分:5)

  

如何扩展聚合?

  • 仔细选择聚合,确保您的命令在多个聚合中合理分布。您不希望拥有可能从并发用户那里获得大量命令的聚合。

  • 序列化发送到聚合实例的命令。这可以通过聚合存储库和命令总线/队列来完成。但对我来说,最简单的方法是使用this post by Michiel Rook

  • 中描述的聚合版本控制进行乐观锁定
  

请求哪个agregate实例?

在我们的reSolve framework中,我们正在为每个命令创建聚合实例,并且不要在请求之间保留它。这种方法的速度非常快 - 获取100个事件并将它们缩减为聚合状态比在集群中找到正确的聚合实例更快。

这种方法是可扩展的,允许你无服务器 - 每个命令一个lambda调用,中间没有共享状态。当聚合具有太多事件时,极少数情况可以通过快照解决。

答案 1 :(得分:4)

  

如何扩展聚合?

Aggregate实例由其事件流表示。每个Aggregate实例都有自己的事件流。来自一个Aggregate实例的事件不会被其他Aggregate实例使用。例如,如果ID = 1的Order Aggregate创建ID = 1001的OrderWasCreated事件,那么该事件将永远不会用于补充其他Order Aggregate实例(ID = 2,3,4 ......)。

话虽如此,您可以通过基于聚合ID在事件存储上创建分片来水平扩展聚合。

  

如果我要创建聚合A的多个实例,如何同步它们?如果其中一个实例处理命令并创建一个事件,那么该事件应该传播到该agregate的每个实例?

你不是。每个Aggregate实例都与其他实例完全分开。

为了能够横向扩展命令处理,建议每次从事件存储中加载一个Aggregate实例,方法是重放其以前生成的所有事件。您可以执行一项优化以提高性能:聚合快照,但建议仅在确实需要时才执行此操作。 This回答可能有所帮助。

  

难道不存在某些业务逻辑要求请求的agregate实例吗?因此,如果我发出多个适用于聚合A(ORDERS)并适用于一个特定订单的命令,则将它传递给同一个实例是有意义的。或?

您假设聚合实例在某些服务器上持续运行'内存。你可以这样做,但这样的架构非常复杂。例如,当其中一台服务器出现故障并且必须由其他服务器替换时会发生什么?很难确定居住在那里的实例并重新启动它们。相反,您可以拥有许多无状态服务器,这些服务器可以处理任何聚合实例的命令。当命令到达时,您识别聚合ID,通过重放其以前的所有事件从事件存储加载它,然后它可以执行命令。执行该命令并将新事件持久保存到Event存储后,您可以丢弃Aggregate实例。到达同一聚合实例的下一个命令可由任何其他无状态服务器处理。因此,可伸缩性仅取决于事件存储本身的可伸缩性。

答案 2 :(得分:0)

  

如何扩展聚合?

系统中的每条信息都有一个逻辑权限。单个数据的多个权限会引起争用。您可以通过创建较小的非重叠边界来扩展写入 - 每个权限都有较小的职责范围

To borrow from your example, an example of smaller responsibilities would
be to shift from one aggregate for all ORDERS to one aggregate for _each_
ORDER.

It's analogous to the difference between having a key value store with
all ORDERS stored in a document under one key, vs each ORDER being stored
using its own key.

读取是安全的,您可以使用多个副本将其缩小。但是,这些副本最终只是一致的。这意味着如果您询问" FCOJ 现在的出价是多少?"你可以从每个副本中得到不同的答案。或者,如果您在10:09:02询问" FCOJ的出价是多少?"那么每个副本都会给你一个答案或说'34;我还不知道"。

  

但是如果粒度已经是每个聚合的一个命令,那么我认为通常不可能实现,并且你有很多并发访问,如何解决它?如何尽可能地分散负载并保持冲突?

粗略草图 - 每个聚合它通过可以根据命令消息的内容计算的密钥存储。通过使用该密钥的比较和交换操作来实现对聚合的更新。

Acquire a message
Compute the storage key
Load a versioned representation from storage
Compute a new versioned representation
Store.compare and swap the new representation for the old

要提供额外的流量吞吐量,您需要添加更多无状态计算。

要提供存储吞吐量,请将密钥分发到更多存储设备。

路由层可用于将消息组合在一起 - 路由器使用与以前相同的存储密钥计算,但使用它来选择计算服务器场中转发消息的位置。然后,计算机可以检查它收到的每批消息的重复密钥,并将这些消息一起处理(交换一些额外的计算以减少比较和交换的数量)。

Sane消息协议很重要;见Marc de Graauw的Nobody Needs Reliable Messaging