【问题标题】:Microservices + CQRS implementation微服务 + CQRS 实现
【发布时间】:2020-09-20 08:55:52
【问题描述】:

我正在使用 CQRS 模式实现微服务架构。我有一个使用 API Gateway、Lambda 和 DynamoDB 的有效实现,但有一个例外 - 事件溯源。

事件溯源让应用程序将通知发布到平台中的其他服务可以使用的事件流。此通知表示作为原始 HTTP 请求的一部分发生的事件。例如,如果用户使用完整的“将患者送入医院”模型进行 HTTP POST,则 Lambda 将分解该模型并按顺序发布多个事件。

患者登记(包括患者 ID、医院 ID + 就诊 ID)

房间分配(包括房间号,+访问ID)

已测试患者(包括已测试 + 就诊 ID)

患者结账(就诊 ID)

此模式的目的是提供对患者住院期间发生的所有事件的审计跟踪。这个示例(不是我实际构建的)将存储在可以随时重放的事件源中。如果在所有服务中都删除了 VisitId,我们可以一次重播一个事件,并按顺序复制原始记录的精确副本。您认为所有记录都是不可变的以实现这一点。每个 POST 都会推入事件源,然后进入数据库,在 HTTP GET 请求期间将数据拉出。它还会有订阅者获取这些数据片段并做其他事情 - 例如“访问调查”服务,该服务将听取患者结账事件并准备术后调查。

我查看了几个 AWS 服务来提供此功能。我知道 Kinesis Data Streams,但我不喜欢定价结构,也不想处理分片(无自动缩放)。由于我的整个平台是建立在基于消费的定价(Dynamo、Lambda 等)之上的,我希望以相同的方式保持我的事件源。这让我可以更轻松地估算每位用户的成本,因为我只是根据每位用户每月的预估请求数进行数学运算。

我一直在将 SNS 用于流本身,传递通知,这很棒。超级快,在开发时没有任何重大问题。但问题是这不适合重播存储 - 仅传递事件消息。对于回放商店,我认为 Kinesis Firehose 很有意义......同时将其发送到 S3 + SNS。事实证明,SNS 不是可用的交付目的地。我可以自己放入 S3,然后发布到 SNS,但这似乎是代码库中的重复工作,因为我可以设置 S3 触发器来触发 Lambda,并且只有另一个小 Lambda 对 S3 中的事件着陆做出反应并执行插入进入 DynamoDB。我已经看到这可能比仅通过 SNS 发布要慢得多。我也不确定 Put 事件的重试策略。这简化了重试,因为我可以重复使用触发的 Lambda 中的代码来重播存储桶路径中的所有事件。

我可以只使用 PutObject,然后在同一个 HTTP POST Lambda 中发布到 SNS。如果 SNS 发布失败,那么我现在在 S3 中有一个从未发布过的对象。我必须编写一个不同的 Lambda 来处理修复和发布。不是世界末日——不管怎样,我都有两个 Lambda 可以部署。我只是不确定在 AWS 服务的这种模式中哪种方式更有意义。

有没有人做过类似的事情并有什么建议?我是否正在努力进入一个以后难以管理的技术漏洞?如果我可以保持基于消费的定价模型,我也愿意接受其他途径。谢谢!

【问题讨论】:

    标签: amazon-s3 microservices amazon-sns cqrs event-sourcing


    【解决方案1】:

    事件溯源让应用程序将通知发布到平台中的其他服务可以使用的事件流。

    您需要在这里小心一点——“事件溯源”至少有两种不同的定义。

    如果您关心事件溯源,通常与 CQRS 相结合(Greg Young 等人),那么您的事件是您的记录簿。这引入的重要复杂性是,您的服务需要能够在对其进行更改时锁定“事件流”(没有该锁定,您会遇到“丢失编辑”的情况并且必须清理混乱)。

    因此,“指向您当前更改的指针”需要存在于具有事务的东西中。 DynamoDB 应该可以解决这个问题(根据我对 re:Invent 2017 上事件溯源会议室的记忆)。在 theory 中,您可以在 dynamo 中使用锁,其中包含指向存储在 S3 中的不可变文档的指针。我无法说服自己权衡取舍证明复杂性是合理的,但我可以说,该架构中没有任何违反物理和因果关系的东西。

    如果您的运营团队对 Dynamo 不满意,另一个合理的选择是 RDS;选择您喜欢的关系数据引擎,为其部署事件存储架构,然后就可以开始了。

    至于 pub sub 部分,我相信你在 SNS 上是正确的。这是将消息从发布者“扇出”到多个消费者的正确选择。是的,它不支持重播,但没关系——重播可以通过从记录簿中拉 事件来进行。请参阅 Greg Young 的 Polyglot Data talk 的后面部分。是的,有时你会在推送通道和拉取通道上都收到消息,但这没关系;当您认为分布式架构是一个好主意时,您已经注册了幂等消息处理。

    编辑

    为什么需要在 DynamoDB 中存储指针?

    因为 S3 不提供任何锁定功能;这意味着在不愉快的路径上,您的逻辑的两个副本试图写入不同版本的数据,您最终会成为丢失编辑问题的受害者。

    您可以使用乐观锁定来管理这种情况——类似于 HTTP 的条件 PUT;但 S3(我上次检查)不支持条件修改。

    您可以将 S3 用作不可变文档的对象存储,但现在您需要一些机制来确定 S3 中的哪个文档是“当前”文档。如果你尝试在 S3 中实现它,你会再次遇到同样的丢失编辑问题。

    所以你需要一个不同的工具来处理这部分问题;一些适合“国家继承”的工具。所以 DynamoDB 很适合。

    如果您使用 DynamoDB 进行锁定,是否也可以将其用于事件存储?我没有足够的圈数来确信我知道那里的答案。对于小问题,我最有信心的答案是肯定的。对于大问题...?

    可能有用的讨论:

    【讨论】:

    • 这里有很好的信息,谢谢。为什么需要在 DynamoDB 中存储指针?我可以使用 S3 作为记录源,并使用命名约定来查询 S3 并重放特定记录的事件。我是否还缺少 Dynamo 或其他数据库获得的其他东西?我敢肯定 S3 会更慢,但在回复场景中,如果我能把它拿回来,性能并不是一个重要的优先事项。在这里,规模似乎也对我有利。如果我把名字搞砸了,我会冒着我整个记录簿的风险。如果您能详细说明,我将不胜感激!
    • 谢谢,是否使用锁定来确保正确捕获事件顺序?我假设每个事件都是不可变的并且永远不会改变,所以锁定并不重要。我可以看到一个场景,虽然我们的 http PATCH 请求会在应用程序批量处理多个 PATCH 请求时创建竞争条件。这会在等待锁定数减少时减慢请求处理服务器端的速度。这是有道理的,但不幸的是。
    • 澄清一点 - 我假设每个事件都是不可变的,并将其作为自己独特的记录放入 S3。不是随时间变化的可变记录。重放将是重放存储桶中的每个对象以进行记录的行为。
    • 感谢您的提示 - 我想我将启用 DynamoDB 流并仅使用 Dynamo 表来锁定和存储事件。然后使用流触发将发布到 SNS 的 Lambda。
    猜你喜欢
    • 2018-08-12
    • 2019-05-17
    • 2021-10-01
    • 2022-11-23
    • 2021-11-14
    • 2018-02-21
    • 2019-11-01
    • 2020-03-04
    • 2018-06-24
    相关资源
    最近更新 更多