这是关于如何使用Spring for Apache Kafka在跨多个微服务的MongoDB中管理分布式数据模型。
由多个微服务组成的现代分布式系统,每个微服务都拥有一个领域的聚合数据的子集,那么该系统几乎肯定会具有某些数据重复,在这种情况下,我们如何保持数据的一致性?
Apache Kafka
Apache Kafka是一个开放源代码的分布式事件流平台,能够处理数万亿条消息。根据Confluent(最初被认为是消息队列)的说法,Kafka基于分布式提交日志的抽象。自2011年由LinkedIn创建并开源以来,Kafka已从消息传递队列迅速发展为成熟的事件流平台。
根据Wikipedia的说法,最终一致性是一种用于分布式计算以实现高可用性的一致性模型,该模型非正式地保证了如果不对给定数据项进行任何新的更新,则最终对该项的所有访问都将返回最后的更新值。
领域驱动设计
为了进行讨论,让我们研究一个常见的示例:在线店面。
使用域驱动设计(DDD)方法,我们希望问题域(在线店面)由多个有界上下文组成。绑定的上下文可能包括购物Order、客户服务、市场营销、安全性、送货Fullfillment、会计等。
给定这个问题域,我们可以假设我们拥有客户的概念。此外,我们可以假设定义客户的唯一属性可能分布在多个有界上下文中。完整的客户视图将要求您汇总来自多个上下文的数据,例如:
- 会计方面可能是记录系统的主要客户信息,如客户的姓名,联系方式,联系方式偏好,以及计费和送货地址。
- 市场营销可能会拥有有关客户使用商店的忠诚度计划和在线购物活动的其他信息。
- 送货上下文可能会保留所有运送给客户的订单的记录。
- 安全性可能会保留客户的访问凭据,帐户访问历史记录和隐私设置。
分布式数据一致性
我们域数据模型需要跨有界上下文甚至是同一上下文中的服务之间的某些数据重复,我们必须确保数据的一致性。以客户更改其家庭住址或电子邮件的情况为例。让我们假设“会计”上下文是这些数据字段的记录系统。但是,要实现订单送货,“送货”上下文可能还需要维护客户的当前家庭住址。同样,负责选择电子邮件广告的Marketing上下文也需要了解电子邮件的更改并更新其客户记录。
如果更改了一条共享数据,则进行更改的一方应负责传达更改,而不会期望得到响应。因为它们说的是事实,而不是问问题,问问题才需要响应回答,因此,感兴趣的各方可以选择是否以及如何对变更通知采取行动。
这种分离的通信模型通常被描述为事件携带状态转移,由ThoughtWorks的马丁·福勒(Martin Fowler)在他有见地的帖子中定义,“事件驱动”是什么意思?。
可以将对数据的更改视为状态更改事件,即包含更改数据的详细信息的事件。巧合的是,Fowler在帖子中使用客户的地址更改作为“事件进行状态转移”的示例。格雷厄姆·布鲁克斯(Graham brooks)也在他的帖子中详细介绍了这一概念,事件携带状态转移模式。
一致性策略
可以采用多种体系结构方法来解决分布式系统中的数据一致性。例如:
- 您可以使用具有共享模式的单个关系数据库来持久化数据,而完全避免使用分布式数据模型。但是,可能会争辩说,使用单个数据库只会使您的分布式系统变回整体monlith。
- 您可以使用变更数据捕获(CDC)来跟踪每个数据库的变更,并将这些变更的记录发送到Kafka主题,以供感兴趣的各方使用。Kafka Connect是一个很好的选择,正如Confluent的Robin Moffatt所著的文章“不再孤岛:如何将数据库与Apache Kafka和CDC集成”中所述。
- 我们可以使用独立于域的其他业务服务的单独的数据服务,其唯一作用是确保跨域的数据一致性。如果消息仍然存在于Kafka中,则该服务具有通过消息重播提供数据可审核性的附加功能。当然,另一组服务会增加系统的操作复杂性。
- 在此帖子的某种程度的简化架构中,业务微服务将通过生产和使用来自其所订阅的多个Kafka主题的消息来维护其各自域之间的一致性。Kafka生产者也可能是我们领域内的消费使用者。
店面示例
在这篇文章中,我们的网上商店API将使用Java构建Spring Boot和OpenJDK的16。我们将通过使用保证分布式数据的一致性发布/订阅模式与Spring的Apache kafka项目。当一块数据由一个Spring Boot微服务改变,如果合适的话,该状态改变将触发一个状态改变事件,这将使用卡夫卡主题与其他微服务共享。
店面订购过程的视图显示在下面的图所示。箭头表示数据的交换。卡夫卡将作为从一个去耦服务的其他同时仍能确保数据分布的一种手段。
鉴于订购的使用情况下,我们将研究的三种服务是构成我们的店面API的交互:会计界范围内的账户服务,履行范围内的配送服务,订单管理范围内的订单服务。我们将研究三个服务如何使用卡夫卡通信状态的变化完全完全的方式(改变他们的数据)给对方。
下面示出了图中的事件在后讨论的子系统之间流动。下面对应的编号,以上述订购过程的编号。我们将着眼于三个事件流2,5和6。我们将模拟事件流3,由购物车服务中创建的订单。
店面微服务
我们将探索这三种微服务中每一种的功能,以及它们如何使用Kafka 2.8共享状态改变事件。每个店面API服务都是使用Spring Boot 2.0和Gradle构建的。每个Spring Boot服务包括Spring Data REST,Spring Data MongoDB,用于Apache Kafka的Spring Cloud Sleuth,SpringFox和Spring Boot Actuator。为了简单起见,Kafka Streams和Spring Cloud Stream的使用不属于本文的一部分。
源代码
店面的微服务源代码可在GitHub上公开获得。可以使用以下命令克隆四个GitHub项目:
git clone --branch 2021-istio \ --single-branch --depth 1 \ https://github.com/garystafford/storefront-demo-accounts.git git clone --branch 2021-istio \ --single-branch --depth 1 \ https://github.com/garystafford/storefront-demo-orders.git git clone --branch 2021-istio \ --single-branch --depth 1 \ https://github.com/garystafford/storefront-demo-fulfillment.git git clone --branch 2021-istio \ --single-branch --depth 1 \ https://github.com/garystafford/storefront-demo.git |