微服务+CQRS实现

Microservices + CQRS implementation

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

事件溯源让应用程序向平台中的其他服务可以使用的事件流发布通知。此通知表示作为原始 HTTP 请求的一部分发生的事件。例如,如果用户创建一个带有完整 "check patient into hospital" 模型的 HTTP POST,那么 Lambda 会将其分解并按顺序发布多个事件。

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

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

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

患者结账(就诊 ID)

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

我查看了几种提供此功能的 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 服务的这种模式中哪种方式更有意义。

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

Event Sourcing has the applications publishing a notification to an event stream that other services in the platform can consume.

你在这里要小心一点——"event sourcing" 运行ning 至少有两种不同的定义。

如果您关心事件溯源,在某种意义上通常与 CQRS(Greg Young 等人)相结合,那么您的事件 就是 您的记录簿。这引入的重要并发症是您的服务需要能够 锁定 对 "event stream" 进行更改时(如果没有该锁定,您 运行 进入 "lost edit"场景,必须收拾残局)。

所以 "pointer to your current changes" 需要住在有交易的地方。 DynamoDB 应该没问题(根据我对 re:Invent 2017 事件源分组讨论室的记忆)。在 理论 中,您可以在 dynamo 中锁定,它包含指向存储在 S3 中的不可变文档的指针。我无法说服自己权衡取舍证明其复杂性是合理的,但据我所知,该架构中没有任何内容违反物理学和因果关系。

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

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

编辑

Why the need to store a pointer in DynamoDB?

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

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

可以将S3用作不可变文档的对象存储,但现在您需要一些机制来确定S3中的哪个文档是"current" 一个。如果您尝试在 S3 中实现它,您 运行 会再次陷入相同的丢失编辑问题。

所以你需要一个不同的工具来处理这部分问题;一些适合 "state succession" 的工具。所以 DynamoDB 适合那里。

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

可能有用的讨论: