一、设计原则之CAP
CAP 原则又称 CAP 定理,指的是在一个分布式系统中,Consistency(一致性)、Availability(可用性)和 Partition tolerance(分区容错性),三者不可兼得,如图所示:
下面对分布式系统中的三个特性进行了归纳
- 一致性(C):在分布式系统中的所有数据备份在同一时刻是否有同样的值
- 可用性(A):在集群中一部分节点故障后,集群整体是否还能响应客户端的读写要求
- 分区容忍性(P):以实际效果而言,分区相当于对通信的时限要求。系统如果不能在时限内达成数据一致性,就意味着发生了分区的情况,必须就当前操作在 C 和 A 之间做出选择
掌握 CAP 定理, 尤其是能够正确理解 C、A、P 的含义,对于系统架构来说非常重要。因为对于分布式系统来说,网络故障在所难免,如何在出现网络故障时,维持系统按照正确的行为逻辑运行就显得尤为重要。可以结合实际的业务场景和具体需求来进行权衡。
例如,对于大多数互联网应用来说,因为及机器数量庞大,部署节点分散,网络故障是常态,可用性是必须要保证的,所以只有舍弃一致性来保证服务的 AP。而对于银行等需要确保一致性的场景,通常会权衡 CA 和 CP 模型,CA 模型网络故障时完全不可用,CP 模型具有部分可用性。所以,在设计微服务的时候一定要选择合适的模型。
CA(consistency + availability),这样的系统关注一致性和可用性,它需要非常严格的全体一致的协议,比如“两阶段提交”(2PC)。CA 系统不能容忍网络错误和节点错误,一旦出现这样的问题,整个系统就会拒绝写请求,因为它并不知道对面的那个结点是否挂掉了,还是只是网络问题。唯一安全的做法就是把自己变成只读的。
CP(consistency + partition tolerance),这样的系统关注一致性和分区容忍性。它关注的是系统里大多数人的一致性协议,比如 Paxos 算法(Quorum 类的算法)。这样的系统只需要保证大多数结点数据一致,而少数的结点会在没有同步到最新版本的数据时变成不可用的状态,这样能够提供一部分的可用性。
AP(availability + partition tolerance) ,这样的系统关心可用性和分区容忍性。因此,这样的系统不能达成一致性,需要给出数据冲突,给出数据冲突就需要维护数据版本,Dynamo 就是这样的系统。
二、设计原则之EDA事件驱动
EDA 是一种以事件为媒介,实现组件和服务之间最大松耦合的方式。传统面向接口编程以接口为媒介,实现调用接口者和接口实现者之间的解耦,但是这种解耦程度不是很高,如果接口发生变化,则双方代码都需要变动,而事件驱动则是调用者和被调用者互相不知道对方,两者只和中间消息队列耦合。
在事件驱动的架构中,跨服务完成业务逻辑的一个关键点是每个服务自动更新数据库和发布事件,也就是以原子粒度更新数据库和发布事件。
保证数据更新和事件发布原子化的方法有以下几种:
- 使用本地事务发布事件
一个实现原子化的方法是使用本地事务来更新业务实体和事件列表,有一个独立进程来发布事件。使用单独的事件表记录事件状态,然后使用单独的进程来监控事件的变化情况,确保事件实时发布。这种方式的缺点是数据更新和事件之间的对应关系是由开发者实现的,极有可能出错。
- 挖掘数据库事务日志
由线程或进程通过挖掘数据库事务或提交日志来发布事件。应用更新数据库,数据库的事务日志会记录这些变更。事务日志挖掘线程或进程读取这些日志,并把时间发布到消息队列。这种方式的优点是不需要开发人员参与;缺点是与数据库紧耦合,需要根据数据库的变化而变化,另外根据数据库日志不一定能推断出所有的业务场景。
- 使用事件源
事件源采用一种截然不同的、以事件为中心的方法来保存业务实体——不同于存储实体的当前状态,应用存储的是状态改变的事件序列。每当业务实体的状态改变,新事件就被附加到事件列表,并且应用可以通过事件回放来重构实体的当前状态。事件长期保存在事件仓库(Event Store),使用 API 添加和检索实体的事件。通过 API 让服务订阅事件,将所有事件传达到所有感兴趣的订阅者。所以,事件仓库可以认为是数据库与消息代理的综合体。缺点是想要构建一个高效的事件仓库并不容易。
参考资料:《微服务架构实战》—— 张锋
一 叶 知 秋,奥 妙 玄 心