微服务架构中的数据一致性( 二 )

微服务架构中的数据一致性

文章插图
图片
事件日志不仅可用于恢复事务处理 , 还可用于为系统用户 , 客户或支持团队提供可见性 。但是,在简单方案中 , 服务日志可能是冗余的,状态端点或状态字段就足够了 。
编配(Orchestration)与编排(choreography)到目前为止,您可能认为sagas只是编配(orchestration )方案的一部分 。但是sagas也可以用于编排(choreography ),每个微服务只知道过程的一部分 。Sagas包括处理分布式事务的正流和负流的知识 。在编排(choreography )中,每个分布式事务参与者都具有这种知识 。
单次写入事件【微服务架构中的数据一致性】到目前为止描述的一致性解决方案并不容易 。他们确实很复杂 。但有一种更简单的方法:一次修改一个数据源 。我们可以将这两个步骤分开,而不是改变服务的状态并在一个过程中发出事件 。
更改为先在主要业务操作中,我们修改自己的服务状态,而单独的进程可靠地捕获更改并生成事件 。这种技术称为变更数据捕获(CDC) 。实现此方法的一些技术是Kafka Connect或Debezium 。
微服务架构中的数据一致性

文章插图
使用Debezium和Kafka Connect更改数据捕获
但是 , 有时候不需要特定的框架 。一些数据库提供了一种友好的方式来拖尾其操作日志,例如MongoDB Oplog 。如果数据库中没有此类功能,则可以通过时间戳轮询更改,或使用上次处理的不可变记录ID查询更改 。避免不一致的关键是使数据更改通知成为一个单独的过程 。在这种情况下,数据库记录是单一的事实来源 。只有在首先发生变化时才会捕获更改 。
微服务架构中的数据一致性

文章插图
无需特定工具即可更改数据捕获
更改数据捕获的最大缺点是业务逻辑的分离 。更改捕获过程很可能与更改逻辑本身分开存在于您的代码库中 - 这很不方便 。最知名的变更数据捕获应用程序是与域无关的变更复制,例如与数据仓库共享数据 。对于域事件,最好采用不同的机制,例如明确发送事件 。
事件第一让我们来看看颠倒的单一事实来源 。如果不是先写入数据库,而是先触发一个事件 , 然后与自己和其他服务共享 。在这种情况下,事件成为事实的唯一来源 。这将是一种事件源的形式 , 其中我们自己的服务状态有效地成为读取模型 , 并且每个事件都是写入模型 。
微服务架构中的数据一致性

文章插图
事件优先方法
一方面 , 它是一个命令查询责任隔离(CQRS)模式,我们将读取和写入模型分开,但CQRS本身并不关注解决方案中最重要的部分 - 使用多个服务来消耗事件 。
相比之下,事件驱动的体系结构关注于多个系统所消耗的事件,但并未强调事件是数据更新的唯一原子部分 。所以我想引入“事件优先”作为这种方法的名称:通过发出单个事件来更新微服务的内部状态 - 包括我们自己的服务和任何其他感兴趣的微服务 。
“事件优先”方法面临的挑战也是CQRS本身的挑战 。想象一下 , 在下订单之前,我们想要检查商品的可用性 。如果两个实例同时收到同一项目的订单怎么办?两者都将同时检查读取模型中的库存并发出订单事件 。如果没有某种覆盖方案,我们可能会遇到麻烦 。
处理这些情况的常用方法是乐观并发:将读取模型版本放入事件中,如果读取模型已在消费者端更新 , 则在消费者端忽略它 。另一种解决方案是使用悲观并发控制,例如在检查项目可用性时为项目创建锁定 。
“事件优先”方法的另一个挑战是任何事件驱动架构的挑战 - 事件的顺序 。多个并发消费者以错误的顺序处理事件可能会给我们带来另一种一致性问题,例如处理尚未创建的客户的订单 。
诸如Kafka或AWS Kinesis之类的数据流解决方案可以保证将按顺序处理与单个实体相关的事件(例如,仅在创建用户之后为客户创建订单) 。例如 , 在Kafka中 , 您可以按用户ID对主题进行分区,以便与单个用户相关的所有事件将由分配给该分区的单个使用者处理,从而允许按顺序处理它们 。相反 , 在Message Brokers中,消息队列具有一个订单 , 但是多个并发消费者在给定顺序中进行消息处理(如果不是不可能的话) 。在这种情况下 , 您可能会遇到并发问题 。


推荐阅读