分布式的本质:利用多台机器上进行计算和存储,为了防止某些台机器发生网络延迟、节点故障等问题,就需要使用”算法或者技术“协调”多台机器来一起工作,确保系统的正确性和健壮性。这个协调按照场景来分有以下几个方面,从业务开发容易理解的角度排序如下
可以看出,其实不知不觉中我们已经身处一个被分布式包围的环境中了,而分布式环境下作为开发最重要,最核心需要解决的问题就是保证系统的正确性和健壮性,为了认识保证系统的正确性和健壮性有多困难,有大佬提出了大名鼎鼎的CAP理论来帮助我们普通开发者认知所面临问题的复杂性,以及指导解决问题的方向
CAP是Consistency、Availability、Partition Tolerance的首字母,不同的资料对这三个词的解释稍有差异,现在更广泛被接收的定义是:在一个分布式系统(指互相连接并共享数据的节点的集合)中,当涉及写操作时,只能保证一致性、可用性和分区容错三者中的两个,另一个必须被牺牲
从解释中不难看出,CAP理论探讨的分布式系统强调**互相连接和数据共享**,也就是说例如Memcache/Redis集群(非主从集群模式)并不是CAP理论探讨的对象,因为不具备相互连接和数据共享的特性,而MySQL集群是CAP理论探讨的对象;CAP理论还提及了当写操作时,也就是说它关注的是写操作而不是分布式系统的全部功能。
CAP理论初期所讨论的数据共享,我认为更多的是同源数据(同副本)在多台主机组成的分布式系统上的数据共享。而在日常开发过程中,这个数据共享,更多的被视为异构数据的共享与交换
集群:多个人在一起做同样的事 ,简单的说就是你负责的某一个应用使用集群方式部署了,多台机器提供的是同一个服务
分布式系统 :多个人在一起做不同的事 ,简单的说就是你负责的多个应用之间如果有相互调用(通信,协同),那这就是一套分布式系统。
微服务是一套架构设计方式:解决的问题是为什么你需要开发多个应用,这些应用之间如何联系上对方的问题;
总结:集群是系统部署方式,解决的是你开发的这些应用如何统一部署的问题,如果统一对外提供服务的问题;分布式是解决你开发的这么多应用,之间如何更好的协同在一起,共同完成某一个目标;因为微服务架构和 集群部署方式的存在,需要考虑服务注册与发现,服务与服务之间的事物管理,服务于服务之间的异构数据的逻辑一致性。
P是不可避免的,在P发生的情况下,那么C和A怎么权衡呢, 不同场景有不同的诉求
Paxos 和Raft 都包含了 领导者选举和日志复制 ,领导者选举通过提议或者其他形式,多台机器之间达成一致选择某一台机器为master, 后面的日志复制是指将一个节点上的操作日志复制到其他节点上,以确保这些节点上的状态与原节点上的状态保持一致.
领导者选举是一种机制,用于在多个节点之间选择一个Leader节点,负责协调集群中的所有操作。在选举时,每个节点都可以成为Candidate,并向其他节点发送投票请求。只有得到大多数节点的选票,Candidate节点才能成为新的Leader节点。该机制的目的是确保集群中只有一个Leader节点,并防止出现分裂大多数节点的情况。领导者选举通常是分布式系统中的一个重要环节,因为它能确保系统的可用性和稳定性。
日志复制则是指将Leader节点的日志复制到其他节点,从而实现数据的一致性。在复制时,Leader节点会将自己的日志复制到所有Follower节点,并等待Follower节点的确认消息。只有当Leader节点接收到大多数Follower节点的确认消息时,才会认为该日志项已经被提交并通知所有节点。该机制的目的是确保所有节点都保存了相同的日志,以保证数据的一致性。
领导者选举和日志复制都是一致性算法中的关键概念,它们都需要多人投票来实现。但它们的目的和作用略有不同,前者用于选择Leader节点,后者用于实现数据的一致性。领导者选举的多人投票考虑的因素更多,如任期/当前节点的sid,事物id等,而日志复制多人投票更多的是一个二阶段prepare 和confirm的过程.
一个ZK集群,ZAB协议来保证一致性是什么意思呢:”ZAB协议分为两个阶段,选主&事物处理“ 正常情况下一直都是进行事物处理,zk集群的多台机器都同意这个请求了,才算事物处理成功,而当ZK本身是一个集群也是一个分布式系统,ZK自身的可用性是通过选主自愈来保障的,而从分裂开始到选主自愈这个过程中日志状态会进行同步, 从而ZK保证了外部系统提交的数据的一致性。从ZK是一个集群形式部署起来,从集群的角度来看统一对外提供某一种服务, 外部系统和ZK之间完成的事情本身就组成了一个分布式系统的问题,当外部系统无法和ZK通信的时候整个系统就无法提供服务了,当外部系统能够和ZK 主通信的时候,主要看ZK能不能对外提供一致性的服务了。
分布式一致性和共识算法是两个概念,但是它们之间存在联系。
分布式一致性是指在分布式系统中,所有节点的数据副本在一定时间内保持相同的状态。它是分布式系统中一个非常重要的问题,因为分布式系统的节点数量庞大,网络通信延迟和故障等问题也难以避免,这会导致节点之间数据的不一致性。
共识算法是分布式一致性的实现方式之一,它通过协调分布式系统中的节点,使得它们在一定时间内达成一致的决策。共识算法的实现方式有很多种,如Paxos、Raft等。
因此,可以说共识算法是实现分布式一致性的一种方式,而分布式一致性则是指分布式系统中所有节点数据的一致性状态。
常用的中间件和它们所使用的一致性算法如下:
不同的中间件使用的一致性算法也不同,它们的使用场景也各不相同,需要根据实际的需求进行选择。
我们的业务系统与业务系统的交互:因为是集群与集群之间的互相连接,某应用A连接应用B的时候连不通了(直接tcp无法建立连接 或者连接超时),这个时候其实分区就发生了, 而因为集群的存在,这分区发生的概率非常小, 这个时候,我们是选择AP还是CP呢?这个要根据业务场景以及当前系统间交互的复杂度而定。
比如在业务系统中,比如在交易订单创建好,由履约系统来监听交易订单支付消息后,数据库中写履约单->调用物流系统来 呼叫运力,触发源是交易订单支付成功消息,触发后的动作有两步 写数据库落履约单&调用物流系统来呼叫运力,一般写代码的初步思考是这两步执行的先后顺序是什么样,这两步一个是和数据库交付,一个是和另外一个域的系统交付,因为网络的不可靠特性(连接超时主被动断开)都可能存在未知(成功或者失败)情况,这个时候分区就发生了,分区发生的时候你怎么处理保证履约单和呼叫运力两个节点的“逻辑状态”是一致的,比如如果这个时候直接对外抛异常:“呼叫运力失败”,按照两步执行的先后顺序,可能发生的情况也不一致
先落履约单状态-后呼叫运力 :落履约单状态失败,直接失败; 落履约单状态成功,呼叫运力未知,履约单状态怎么处理(回滚or不会滚)? 这里存在的可能不一致是履约单状态和物流系统的状态的不一致,单独履约单状态进行回滚或者不回滚都不对,因为对你的当次请求来说是未知的. 这里都是阻塞了当次请求,可以看做是想要使用CP模式. 而面对这种未知情况,C怎么保证呢
先呼叫运力-后落履约单状态:呼叫运力未知,怎么处理;落履约单状态失败。呼叫运力未知怎么处理,这里存在可能不一致是履约单状态和物流系统状态的不一致;呼叫运力成功了,落履约单状态失败了,这里存在一定存在的不一致是 物流系统状态和履约单状态的不一致. 这里都是阻塞了当次请求,可以看做是想要使用的CP模式,而面对这种未知情况,C怎么保证呢
如果没有技术手段/策略/算法来解决这个C不一致问题, 就无法保证系统的健壮性和稳定性。而这个解决的过程因为存在多个系统的调用,绝不是单独靠一方就能解决,需要多个系统互相配合进行解决。让我们试着解决一下上面的问题.
履约业务的特殊性:呼叫物流运力之后,物流运力会实时通过消息告知这个单子的一个状态流转情况:”运单创建”,”配送商接单”,”骑手接单”,”骑手到店”,”骑手取餐“,”配送完成/失败“. 第一个状态”运单状态“可以看做是履约和物流系统之间的二阶段握手确认状态,后续的状态是 业务逻辑的状态.
先落履约单状态-后呼叫运力 : 整体思路采用二阶段状态的方式落履约单状态的时候落的是预呼单成功的状态,呼叫运力未知的情况下,如果后续接收到了”运单创建“ 则确认呼叫运力是成功的。如果没有接收到”运单创建“ 状态,则本次呼叫一直是失败的,并需要外部人工或者系统自动捞起呼单,同时需要下游物流系统保证幂等.
先呼叫运力-后落履约单状态:呼叫运力未知,怎么处理;和上面一样,运单创建状态直接变为呼叫运力成功。
对于多次无法配送成功,需要多次呼叫运力的情况,可以采用的是requestId的形式, 在呼叫运力的时候带上requestId,也即是在 ”运单创建“ 回传的节点及后续每一次回传,,需要把requestId 带回来,根据requestId 匹配 本次呼叫和上一此呼叫的二阶段确认.
上述是两种针对在P发生的时候,保证C的技术手段.
最后:当你写代码的时候思考,调用下游”写数据”失败(明确失败/超时)了怎么处理的时候,你其实就是在考虑CP还是AP的问题了,在这一个请求中比如你有兜底或者兼容逻辑这应该就是AP ,在这一个请求中直接报错了,这就是CP(明确失败)了. 一般读数据不会碰到这类问题
术法万千,道法归一, 在做技术的过程中,术能提高我们的效率,成为一个高效的程序员,但是只有不断思考认识到术后面的道,才能更好地理解技术与现实