首页 文章

CQRS,事件采购和扩展

提问于
浏览
6

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

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

  • 不应该有一些业务逻辑存在要求的agregate实例?因此,如果我发出适用于 aggregate A (ORDERS) 的多个命令并应用于一个特定的顺序,则将它传递给同一个实例是有意义的 . 要么?

在本文中:https://initiate.andela.com/event-sourcing-and-cqrs-a-look-at-kafka-e0c1b90d17d8,他们正在使用Kafka进行分区 . 因此,用户管理服务 - 聚合被缩放,但仅订阅主题的特定分区,其包含特定用户的所有事件 .

谢谢!

3 回答

  • 3

    如何扩展聚合?

    • 仔细选择聚合,确保您的命令在许多聚合中合理分布 . 您不希望拥有可能从并发用户接收大量命令的聚合 .

    • Serialize发送到聚合实例的命令 . 这可以通过聚合存储库和命令总线/队列来完成 . 但对我来说,最简单的方法是使用this post by Michiel Rook中描述的聚合版本控制进行乐观锁定

    请求哪个agregate实例?

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

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

  • 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)并适用于一个特定订单的命令,则将它传递给同一个实例是有意义的 . 要么?

    您假设聚合实例在某些服务器上连续运行' RAM. You could do that but such an architecture is very complex. For example, what happens when one of the servers goes down and it must be replaced by other? It'很难确定生活在哪里的实例并重新启动它们 . 相反,您可以拥有许多无状态服务器,可以处理任何聚合实例的命令 . 当命令到达时,您识别聚合ID,通过重放其所有先前的事件从事件存储加载它,然后它可以执行命令 . 执行该命令并将新事件持久保存到Event存储后,您可以丢弃Aggregate实例 . 到达同一聚合实例的下一个命令可以由任何其他无状态服务器处理 . 因此,可伸缩性仅取决于事件存储本身的可伸缩性 .

  • 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的报价是多少?" you may get different answers from each copy. Alternatively, if you ask " FCOJ的报价在10:09:02是什么时候?" then each copy will either give you a single answer or say "我还不知道” .

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

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

    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 .

相关问题