分布式
TOC
[TOC]
分布式系统定义及面临的问题
分布式系统定义为:
分布式系统是一个硬件或软件组件分布式在不同的网络计算机上,彼此之间仅仅通过消息传递进行通信和协调的系统。
分布式环境的各种问题
通信异常
网络分区
三态
分布式系统每一个请求与响应存在特有的“三态”概念,即成功、失败和超时。
节点故障
分布式理论:一致性概念
不同场景对分布式一致性的需求是不同的。
火车站售票系统,要求数据是实时一致;
转账系统要求最终一致,但是要正确;
网购系统要求最终一致,中间会存在不一致的状态,但是最终也会是一致的。
分布式一致性的提出
数据库之间复制的延时。
为什么要数据复制:
- 增加系统可用性,防止单点故障导致系统不可用
- 可以通过负载均衡,提高系统整体性能
数据一致性,是指对一个副本数据进行更新的时候,必须确保也能更新其他的副本,否则不同副本之间的数据将不一致。
一致性级别:
-
强一致性
-
弱一致性(尽可能保证一定时间内达到数据一致)
-
最终一致性(一定保证一定时间内达到数据一致)
分布式事务
是指事务的参与者、支持事务的服务器、资源服务器以及事务管理器分别位于分布式系统的不同节点上,通常一个分布式事务会涉及到多个数据源或业务系统的操作。
分布式理论-CAP定理
什么是CAP定理
CAP理论告诉我们,一个分布式系统,不可能同时满足一致性(C:Consistency),可用性(A:Availability)和分区容错性(P:Partition tolerance)这三个基本需求,最多只能同时满足其中两个。

对于分布式系统而言,分区容错性可以说是一个基本的要求,因此架构师往往需要把精力花在如何根据业务特点在C(一致性)和A(可用性)之间寻求平衡。
能不能解决3选2的问题
需要思考分区是不是百分之百出现呢?
如果不出现,那么就能够同时满足CAP。
如果出现了分区,可以根据策略进行调整。比如C不必使用那么强的一致性,可以先将数据存起来,稍后更新,实现所谓的“最终一致性”。
基于这个思路,也引出了第二个理论Base理论。
分布式理论-BASE理论
什么是BASE理论
全称:Basically Available(基本可用),Soft state(软状态),和Eventually consistent(最终一致)三个短语的缩写,来自ebay的架构师提出。
Base理论和CAP理论的关系
Base理论是对CAP中一致性和可用性权衡的结果,其来源于大型互联网分布式实践的总结,是基于CAP定理逐步演化而来的。
Basically Available(基本可用)
分布式系统在出现不可预知的故障时,允许损失部分可用性。而不是完全不可用。具体举两个例子:
- 响应时间上的损失:正常情况下,一个在线搜索引擎要在0.5秒内返回用户相应结果,但由于出现故障,查询结果的响应时间增加到了1-2秒
- 功能上的损失:对于电商网站,在大促时,由于消费者的购物行为激增,为了保护系统的稳定性(或者保证一致性),部分消费者可能会被引导到一个降级页面。比如如下页面
Soft state(软状态)
什么是软状态?相对于一致性,要求多个节点的数据副本都是一致的,这是一种“硬状态”。
软状态是指:允许系统中的数据存在中间状态,并认为该状态不影响系统整体可用性,即允许系统在多个不同节点的数据副本之间进行数据同步的过程中存在延迟。
Eventually consistent(最终一致)
强调的是系统中所有的数据副本,在经过一段时间的同步后,最终能够达到一个一致的状态。因此最终一致性的本质是需要系统保证最终数据能够达到一致,而不需要实时保证系统数据的强一致性。
在实际工程实践中,最终一致性存在以下五类主要变种:
-
因果一致性(Causal consistency)
-
读己之所写(Read your writes)
-
会话一致性(Session consistency)
-
单调读一致性(Monotonic read consistency)
-
单调写一致性(Monotonic write consistency)
实际系统实践中,可以将其中若干个变种互相结合起来,以构建一个具有最终一致性的分布式系统。
总结:BASE理论面向的是大型高可用可扩展的分布式系统,通过牺牲强一致性来获得可用性,并允许数据在一段时间是不一致的,但最终要保证数据一致。
分布式理论-一致性协议2PC
背景:为了使系统尽量能够达到CAP,于是有了BASE协议,而BASE协议是在可用性和一致性之间做的取舍和妥协。
我们在对分布式系统进行架构设计的时候,对可用性和一致性反复权衡的过程中,涌现了一些经典的算法和协议,最著名的几种就是二阶段提交协议,三阶段提交协议,Paxos算法等。
什么是2PC
协调者:统一调度所有分布式节点的执行逻辑。负责调度参与者的行为,并最终决定这些参与者是否要把事务真正进行提交。
参与者:被调度的节点
二阶段提交就是把事务的提交过程分成了两个阶段来进行处理。流程如下:
阶段一:提交事务请求

- 事务询问
协调者向所有参与者发送事务内容,询问是否可以执行事务提交操作,并开始等待各参与者的响应。
- 执行事务
各参与者节点执行事务操作,并将Undo和Redo信息记入事务日志中
(Redo用来保证事务的原子性和持久性,Undo能保证事务的一致性,两者也是系统恢复的基础前提)
- 各参与者向协调者反馈事务询问的响应
如果参与者成功执行了事务操作,那么久反馈给协调者Yes响应,表示事务可以执行;如果参与者没有成功执行事务操作,就反悔No给协调者,表示事务不可以执行。
又称为投票阶段,即各参与者投票表明是否要继续执行接下来的事务提交操作。
阶段二:执行事务提交
根据阶段一的结果决定是否可以进行事务提交操作,正常情况下,包含两种操作可能:提交事务,中断事务。
提交事务过程如下:
假如阶段一所有参与者反馈都是Yes,那么就会执行事务的提交

- 发送提交请求
协调者向所有参与者发送commit请求
- 事务提交
参与者收到commit请求后,会正式执行事务提交操作,并在完成提价之后释放整个事务执行期间占用的事务资源。
- 反馈事务提交结果
参与者在完成事务提交之后,向协调者发送Ack信息
- 完成事务
协调者接收到所有参与者反馈的Ack信息后,完成事务。
中断事务步骤如下:
假如任何一个参与者向协调者反馈了No响应,或者在等待超时后,协调者尚未接收到所有参与者的反馈响应,那么就会中断事务。

- 发送回滚请求
协调者向所有参与者发送Rollback请求
- 事务回滚
参与者接收到Rollback请求后,会利用其在阶段一记录的Undo信息来执行事务回滚操作,并在完成回滚之后释放在整个事务执行期间占用的资源
- 反馈事务回滚的结果
参与者在完成事务回滚后,向协调者发送Ack信息
- 中断事务
协调者接收到所有的参与者反馈的Ack信息后,完成事务中断。
2PC主要做了两个事情:投票,执行。
将事务的处理过程分为了投票和执行两个阶段,核心是对每个事务都采用先尝试后提交的处理方式,从而保证其原子性和一致性,因此可以将二阶段提交看成一个强一致性的算法。
2PC优缺点
优点:
原理简单,实现方便
缺点:
同步阻塞,单点问题,数据不一致,过于保守
- 同步阻塞
各个参与者在等待其他参与者响应的过程中,无法进行其他操作。限制了分布式系统的性能。然而分布式系统就是为了提高系统整体的性能。
- 单点问题
协调者单点问题
- 数据不一致
协调者向所有参与者发送commit请求后,发生了局部网络异常或者协调者在尚未发送完所有commit请求之前自身发生了崩溃,最终导致只有部分参与者收到了commit请求。将导致严重的数据不一致问题。
- 过于保守
协调者通过超时机制判断是否需要中断事务,这种策略过于保守。
二阶段没有设计较为完善的容错机制,任意一个节点失败都会导致整个事务的失败。
分布式理论-一致性协议3PC
背景:为了弥补阶段二提交的缺点
什么是三阶段提交
将2PC的提交事务请求过程一分为二,共形成了由CanCommit、PreCommit和doCommit三个阶段组成的事务处理协议。

阶段一:CanCommit
- 事务询问
协调者向所有参与者发送一个包含事务内容的canCommit请求,询问是否可以执行事务提交操作,并开始等待各参与者的响应
- 各参与者向协调者反馈事务询问的响应
参与者在接收到来自协调者的包含了事务内容的canCommit请求后,正常情况下,如果自身认为可以顺利执行事务,则反馈Yes响应,并进入预备状态,否则反馈No响应
阶段二:PreCommit
协调者在得到所有参与者的响应之后,根据结果有两种执行操作的情况:执行事务预提交,或者中断事务
假如所有参与者反馈都是Yes,那么就会执行事务预提交
执行事务预提交分为三个步骤:
- 发送预提交请求
协调者向所有参与者节点发送preCommit请求,并进入prepared阶段。
- 事务预提交
参与者接收到preCommit请求后,会执行事务操作,并将Undo和Redo信息记录到事务日志中。
- 各参与者向协调者反馈事务执行的结果
若参与者成功执行了事务操作,那么反馈Ack,同时等待最终的指令:提交或终止
若任一参与者反馈了No响应,或者在等待超时后,协调者尚未接收到所有参与者反馈,则中断事务
中断事务也分为两个阶段:
-
发送中断请求
-
中断事务
阶段三:doCommit
这个阶段,做真正的事务提交完成回滚
执行事务提交:
- 发送提交请求
- 事务提交
- 反馈提交结果
- 完成事务
中断事务:
-
发送中断请求:协调者向所有参与者节点发送abort请求。
-
事务回滚
-
反馈事务回滚结果
-
中断事务
一旦进入阶段三,可能会出现两种故障:
- 协调者出现问题
- 协调者和参与者之间的网络故障
如果出现任意一种情况,最终都会导致参与者无法收到doCommit请求或者abort请求,针对这种情况,参与者都会在等待超时之后,继续进行事务提交。
3PC优缺点
优点
最大的优点是降低了参与者的阻塞范围(第一阶段不阻塞),其次能够在单点故障后继续达成一致(2PC在提交阶段会出现此问题,3PC会根据协调者的状态进行回滚或者提交)。
缺点
如果参与者收到了preCommit消息后,出现了网络分区,此时协调者所在节点和参与者所在节点无法进行网络通信,那么参与者等待超时后,会进行事务提交,这必然会出现分布式数据不一致的问题。
2PC和对比3PC
-
对于协调者和参与者都设置了超时机制(而2PC只有协调者有超时机制,即如果在一定时间内,没有收到参与者的消息则默认失败)。
-
在2PC准备阶段和提交阶段之间,插入了预提交阶段,是一个缓冲,保证了最后提交阶段之前各个参与节点的状态是一致的。
分布式理论:一致性算法Paxos
什么是Paxos算法
一种基于消息传递且具有高度容错特性的一致性算法。主要用来解决分布式系统中,如何就某个值达成一致的算法。
Google Chubby
OceanBase
Paxos解决了什么问题
解决了一致性问题

在分布式系统中,发生诸如机器宕机或网络异常(包括消息的延迟、丢失、重复、乱序、网络分区)等情况。可以快速且准确的在集群内对某个数据的值达成一致,并且保证不论发生以上任何异常,都不会破坏整个系统的一致性。
假如,一个集群环境中,要求所有机器上的状态是一致的,其中有两台机器想修改某一个状态,机器A想把状态修改为A,机器B想把状态修改为B,那么到底听谁的?
进一步思考,可以引入协调者,谁先到,听谁的。
但是协调者宕机了呢?
所以需要对协调者也做备份也要做集群,这时候,问题来了,这么多协调者,听谁的呢?

Paxos算法就是为了解决这个问题而生的
Paxos相关概念
提案(Proposal):最终要达成一致的value就在提案里。包括提案编号(Proposal ID)和提议的值(Value)
三种角色:
- Proposer:提案发起者
- Acceptor:决策者,可以批准提案,接受提案
- Leaners:最终决策的学习者
如果某个提案被选定,那么该提案的value就被选定了。
具体的实现中,一个进程可能同时充当多种角色。可能即是Proposer又是Acceptor又是Leaner。
怎么理解“对某个数据的值达成一致”?
指的是Proposer、Acceptor、Leaner都认为同一个value被选定。
那么Proposer、Acceptor、Leaner分别在什么情况下才能认为某个value被选定呢?

问题描述
假设有一组可以提出提案的进程集合,那么对于一个一致性算法需要保证以下几点:
- 在这些被提出的提案中,只有一个提案会被选定
- 如果没有提案被提出,就不应该有被选定的提案
- 当一个提案被选定后,那么所有进程都应该能学习(learn)到这个被选定的value
推导过程
最简单的方案,只有一个Acceptor。

多个Acceptor

提出假设,从假设得到的约束提交

上面是因为“一个提案只要被一个Acceptor接受,则该提案的value就被选定”才导致出现不一致的问题。因此,我们需要加一个规定:
规定:一个提案被选定需要过半数以上的Acceptor接受
上面的规定又暗示:一个Acceptor必须能够接受不止一个提案!
不然可能导致最终没有value被选定。
所以在这种情况下,我们使用一个全局的编号来标识每一个Acceptor批准的提案,当一个具有某value值的提案被半数以上Acceptor批准后,就认为该value被选定了,此时也认为该提案被选定了。
但是强调下,提案和value不是同一个概念,提案=提案编号+value
。
根据上面的内容,我们现在虽然允许多个提案被选定,但必须保证所有被选定的提案都具有相同的value值,否则又会出现不一致。
于是有了下面的约束:
P2:如果某个value为v的提案被选定了,那么每个编号更高的被选定提案的value必须也是v。
一个提案只有被Acceptor接受才可能被选定,因此我们可以把P2约束改写成对Acceptor接受的提案的约束P2a。
P2a:如果某个value为v的提案被选定了,那么每个编号更高的被Acceptor接受的提案的value必须也是v。
只要满足了P2a,就能满足P2。

上图会制造一个矛盾。虽然过半数Acceptor认为V1被选定,但是宕机恢复的Acceptor1接受了Proposer1提出的编号更新的提案的V2。
需要对P2a约束进行强化!
P2a是对Acceptor接受的提案约束,但其实提案是Proposer提出来的,所以我们可以对Proposer提出的提案进行约束。得到P2b:
P2b:如果某个value为v的提案被选定,那么之后任何Proposer提出的编号更高的提案的value必须也是v。
由P2b可以推出P2a进而推出P2。
那么如何达到P2b的约束呢?
主要满足P2C即可:

Proposer生产提案

Acceptor接受提案

P1a:一个Acceptor只要尚未响应过任何编号大于N的Prepare请求,那么他就可以接受这个编号为N的提案。
算法优化

如果Acceptor收到一个编号为N的Prepare请求,在此之前他已经响应过编号大于N的Prepare请求。根据P1a,该Acceptor不可能接受编号为N的提案。因此,该Acceptor可以忽略编号为N的Prepare请求。
Paxos算法描述


Leaner学习被选定的value
上面介绍了如何来选定一个提案,下面看看如何让Leaner获取提案,大体有三种方案

如何保证Paxos算法的活性
活性:最终一定会发生的事情:最终一定要选定value

总结:

分布式理论:一致性算法Raft
什么是Raft算法
Raft是一种为了管理复制日志的一致性算法。
Raft将一致性算法分解成几个关键模块,领导人选举、日志复制和安全性
通过实施一个更强的一致性来减少需要考虑的状态的数量。
Raft算法分为两个阶段,首先是选举过程,然后在选举出来的领导人带领下进行正常操作,比如日志复制。
领导人选举
Raft通过选举一个领导人,然后给予他全部的管理复制日志的责任来实现一致性。
在Raft中,任何时候一个服务器都可以扮演下面的角色之一:
- 领导者:处理客户端交互,日志复制等操作,一般一次只有一个领导者
- 候选者:候选者就是选举过程中提名自己的实体,一旦选举成功,则成为领导者
- 跟随者:类似选民,完全被动的角色,这样的服务器等待被通知投票
而影响他们身份变化的则是选举。
Raft使用心跳机制来触发选举。
当server启动时,初始状态都是follower。每一个server都有一个定时器,超时时间为election timeout(一般为150-300ms),如果某server没有超时的情况下收到来自领导者或者候选者的任何消息,定时器重启;如果超时,它就开始一次选举。
下面用图示展示这个过程:
- 任何一个服务器都可以成为候选者,它向其他服务器(选民)发出选举自己的请求,如图

- 其他服务器同意了,回复OK指令,如图所示。

此时如果有一个Follower服务器宕机,没有收到请求选举的要求,则只要达到半数以上的票数,候选人还是可以成为领导者。
- 这样,这个候选者就成为领导者,它可以向选民发出要执行具体操作的指令,比如进行日志复制。

而如果领导者宕机,会引发新的选举,所以,整个集群在选举和正常运行两个状态之间切换,具体如下图:

从上图可以看出,选举和正常运行之间切换,但请注意,上图中的term3有一个地方,后面没有跟着正常阶段,为什么呢?
答:当一次选举失败(比如正巧每个人都透了自己),就执行一次加时赛,每个server会在一个随机的时间重新投票,这样就能保证不冲突了。所以,当term3选举失败,等了几十毫秒,执行term4选举,并成功选举出领导人。
接着,领导者周期性的向所有跟随者发送心跳包来维持自己的权威。
如果一个跟随者在一段时间内没有收到任何信息,也就是超时,那么他就会认为系统中没有可用的领导者,并且发起选举,以选出新的领导者。
要开始一次选举过程,跟随者要增加自己的当期任期号并转换到候选人状态。然后请求其他服务器为自己投票。那么会产生3种结果:
- 自己成功当选
- 其他服务器成为领导者
- 僵住,没有任何一个人成为领导者
注意:
- 每一个server最多在一个任期内投出一张选票(有任期号约束),先到先得。
- 要求最多只能有一个人赢的选票。
- 一旦成功,立即成为领导人,然后广播所有服务器停止投票阻止新的领导者产生。
僵住怎么办?raft通过使用随机选举超时时间方法将服务器打散投票。每个候选人在僵住的时候会随机从一个时间开始重新选举。
以上,就是Raft所有关于领导选举的策略。
日志复制(保证数据一致性)
Leader选出后,就开始接收客户端请求。Leader把请求作为日志条目加入到它的日志中,然后并行的向其他服务器发起AppendEntries RPC复制日志条目。当这条日志被复制到大多数服务器上,Leader将这条日志应用到它的状态机并向客户端返回执行结果。
下图表示了当一个客户端发送一个请求给领导者,随后领导者复制给跟随者的整个过程。

4个步骤:
- 客户端的每一个请求都包含被复制状态机执行的指令
- leader把这个指令作为一条新的日志添加到日志中,然后并行发起RPC给其他服务器,让他们复制这条信息
- 跟随者响应ACK,如果follower宕机或者运行缓慢或者丢包,leader会不断重试,直到所有的follower最终都复制了所有的日志条目。
- 通知所有Follower提交日志,同时领导人提交这条日志到自己的状态机中,并返回给客户端。
可以看到,直到第四步骤,整个事务才会达成。中间任何一个步骤发生故障,都不会影响日志的一致性。
分布式系统设计策略
分布式系统本质是通过将低廉的硬件攒在一起以获得更好的吞吐量、性能以及可用性等。
在分布式环境下,有几个问题是普遍关心的,我们称之为设计策略:
- 如何检测当前节点还活着?
- 如何保障高可用
- 容错处理
- 负载均衡
心跳检测
心跳,就是以固定的频率向其他节点汇报当前节点状态的方式。收到心跳,一般可以认为一个节点和现在的网络拓扑是良好的。当然,心跳汇报时,一般也会携带一些附加的状态、元数据信息,以便管理。
心跳不是万能的,收到心跳可以确认节点正常,但是收不到心跳也不能认为该节点就已经宣告"死亡"。此时,可以通过一些方法帮助Server做决定:周期检测心跳机制、累计失效检测机制。
周期检测心跳机制
Server端每间隔t秒向Node集群发起监测请求,设定超时时间,如果超过超过时间,则判断“死亡”。
累计失效检测机制
在周期监测心跳机制基础上,统计一定周期内节点的返回情况(包括超时及正确返回),以计算节点的死亡概率。另外,对于宣告“濒临死亡”的节点可以发起有限次数的重试,以作进一步判断。
高可用设计
经过设计来减少系统不能提供服务的时间。
系统高可用性常用设计模式包括三种:主备(Master-Slave)、互备(Active-Active)和集群(Cluster)模式。



容错性
容错的处理是保障分布式环境下相应系统的高可用或者健壮性,一个典型的案例就是对于缓存穿透问题的解决方案。
具体看个例子,如图所示:

如果我们查询的某一数据在缓存中一直不存在,就会造成每一次请求都查询DB,这样缓存就失去了意义,在流量大时,或者有人恶意攻击,如频繁发起id为-1的条件进行查询,可能DB就挂了。
那这种问题有什么好的解决办法吗?
一种方法可以缓存不存在的值,比如,key="null"。
负载均衡
关键在于使用多台集群服务器共同分担计算任务,把网络请求及计算分配到集群可用的不同服务器节点上,从而达到高可用性及较好的用户操作体验。

分布式架构网络通信
Java领域通信的技术,有RMI、ESB、Hession、SOAP和JMS等,它们背后到底是基于什么原理实现的呢?
基本原理
网络通信就是将流从一台计算机传输到另一台计算机,基于传输协议和网络IO来实现。
传输协议:TCP、UDP
网络IO:bio、nio、aio
什么是RPC
RPC全称为remote procedure call,即远程过程调用。
借助RPC可以做到像本地调用一样调用远程服务,是一种进程间的通信方式。
RPC本身不是具体的技术,是整个网络远程调用过程。
RPC架构
核心组件
- 客户端(Client),服务的调用方
- 客户端存根(Client Stub),存放服务端端地址消息,再将客户端的请求参数打包成网络消息,然后通过网络远程发送给服务方。
- 服务端(Server),真正的服务提供者。
- 服务端存根(Server Stub),接收客户端发送过来的消息,将消息解包,并调用本地的方法。



对于RPC框架而言,核心模块就是通讯和序列化。
RMI
Remote Method Invocation,是Java原生支持的远程调用,才用JRMP作为通信协议。主要用于不同虚拟机间的通信。可以理解为一个虚拟机上的对象,调用另一个虚拟机上的对象。
核心概念

以一个案例来讲解RMI的使用
案例步骤

代码实现

其中有一个引用对象作为参数






服务端

客户端

实现原理

BIO、NIO、AIO
以内存为目标,数据进内存叫IN,出内存叫OUT
同步和异步
不能光从字母意思了解,要从分布式的角度思考。
同步异步关注的是消息通信机制。
同步:

异步:

阻塞和非阻塞

BIO
NIO
AIO
Netty

Time waits for no one.