auto commit
249
notes/一致性.md
Normal file
@ -0,0 +1,249 @@
|
||||
<!-- GFM-TOC -->
|
||||
* [一、CAP](#一cap)
|
||||
* [一致性](#一致性)
|
||||
* [可用性](#可用性)
|
||||
* [分区容忍性](#分区容忍性)
|
||||
* [权衡](#权衡)
|
||||
* [二、BASE](#二base)
|
||||
* [基本可用](#基本可用)
|
||||
* [软状态](#软状态)
|
||||
* [最终一致性](#最终一致性)
|
||||
* [三、2PC](#三2pc)
|
||||
* [运行过程](#运行过程)
|
||||
* [存在的问题](#存在的问题)
|
||||
* [四、Paxos](#四paxos)
|
||||
* [执行过程](#执行过程)
|
||||
* [约束条件](#约束条件)
|
||||
* [五、Raft](#五raft)
|
||||
* [单个 Candidate 的竞选](#单个-candidate-的竞选)
|
||||
* [多个 Candidate 竞选](#多个-candidate-竞选)
|
||||
* [日志复制](#日志复制)
|
||||
* [参考资料](#参考资料)
|
||||
<!-- GFM-TOC -->
|
||||
|
||||
|
||||
# 一、CAP
|
||||
|
||||
分布式系统不可能同时满足一致性(C:Consistency)、可用性(A:Availability)和分区容忍性(P:Partition Tolerance),最多只能同时满足其中两项。
|
||||
|
||||
<div align="center"> <img src="../pics//f1109d04-3c67-48a3-9963-2c475f94e175.jpg" width="500"/> </div><br>
|
||||
|
||||
## 一致性
|
||||
|
||||
一致性指的是多个数据副本是否能保持一致的特性。
|
||||
|
||||
在一致性的条件下,系统在执行数据更新操作之后能够从一致性状态转移到另一个一致性状态。
|
||||
|
||||
对系统的一个数据更新成功之后,如果所有用户都能够读取到最新的值,该系统就被认为具有强一致性。
|
||||
|
||||
## 可用性
|
||||
|
||||
可用性指分布式系统在面对各种异常时可以提供正常服务的能力,可以用系统可用时间占总时间的比值来衡量,4 个 9 的可用性表示系统 99.99% 的时间是可用的。
|
||||
|
||||
在可用性条件下,系统提供的服务一直处于可用的状态,对于用户的每一个操作请求总是能够在有限的时间内返回结果。
|
||||
|
||||
## 分区容忍性
|
||||
|
||||
网络分区指分布式系统中的节点被划分为多个区域,每个区域内部可以通信,但是区域之间无法通信。
|
||||
|
||||
在分区容忍性条件下,分布式系统在遇到任何网络分区故障的时候,仍然需要能对外提供一致性和可用性的服务,除非是整个网络环境都发生了故障。
|
||||
|
||||
## 权衡
|
||||
|
||||
在分布式系统中,分区容忍性必不可少,因为需要总是假设网络是不可靠的。因此,CAP 理论实际在是要在可用性和一致性之间做权衡。
|
||||
|
||||
可用性和一致性往往是冲突的,很难都使它们同时满足。在多个节点之间进行数据同步时,
|
||||
|
||||
- 为了保证一致性(CP),就需要让所有节点下线成为不可用的状态,等待同步完成;
|
||||
- 为了保证可用性(AP),在同步过程中允许读取所有节点的数据,但是数据可能不一致。
|
||||
|
||||
<div align="center"> <img src="../pics//0b587744-c0a8-46f2-8d72-e8f070d67b4b.jpg" width="500"/> </div><br>
|
||||
|
||||
# 二、BASE
|
||||
|
||||
BASE 是基本可用(Basically Available)、软状态(Soft State)和最终一致性(Eventually Consistent)三个短语的缩写。
|
||||
|
||||
BASE 理论是对 CAP 中一致性和可用性权衡的结果,它的理论的核心思想是:即使无法做到强一致性,但每个应用都可以根据自身业务特点,采用适当的方式来使系统达到最终一致性。
|
||||
|
||||
<div align="center"> <img src="../pics//bc603930-d74d-4499-a3e7-2d740fc07f33.png" width="250"/> </div><br>
|
||||
|
||||
## 基本可用
|
||||
|
||||
指分布式系统在出现故障的时候,保证核心可用,允许损失部分可用性。
|
||||
|
||||
例如,电商在做促销时,为了保证购物系统的稳定性,部分消费者可能会被引导到一个降级的页面。
|
||||
|
||||
## 软状态
|
||||
|
||||
指允许系统中的数据存在中间状态,并认为该中间状态不会影响系统整体可用性,即允许系统不同节点的数据副本之间进行同步的过程存在延时。
|
||||
|
||||
## 最终一致性
|
||||
|
||||
最终一致性强调的是系统中所有的数据副本,在经过一段时间的同步后,最终能达到一致的状态。
|
||||
|
||||
ACID 要求强一致性,通常运用在传统的数据库系统上。而 BASE 要求最终一致性,通过牺牲强一致性来达到可用性,通常运用在大型分布式系统中。
|
||||
|
||||
在实际的分布式场景中,不同业务单元和组件对一致性的要求是不同的,因此 ACID 和 BASE 往往会结合在一起使用。
|
||||
|
||||
# 三、2PC
|
||||
|
||||
两阶段提交(Two-phase Commit,2PC)
|
||||
|
||||
主要用于实现分布式事务,分布式事务指的是事务操作跨越多个节点,并且要求满足事务的 ACID 特性。
|
||||
|
||||
通过引入协调者(Coordinator)来调度参与者的行为,,并最终决定这些参与者是否要真正执行事务。
|
||||
|
||||
## 运行过程
|
||||
|
||||
### 1. 准备阶段
|
||||
|
||||
协调者询问参与者事务是否执行成功,参与者发回事务执行结果。
|
||||
|
||||
<div align="center"> <img src="../pics//04f41228-375d-4b7d-bfef-738c5a7c8f07.jpg" width=""/> </div><br>
|
||||
|
||||
### 2. 提交阶段
|
||||
|
||||
如果事务在每个参与者上都执行成功,事务协调者发送通知让参与者提交事务;否则,协调者发送通知让参与者回滚事务。
|
||||
|
||||
<div align="center"> <img src="../pics//2991c772-fb1c-4051-a9c7-932b68e76bd7.jpg" width=""/> </div><br>
|
||||
|
||||
需要注意的是,在准备阶段,参与者执行了事务,但是还未提交。只有在提交阶段接收到协调者发来的通知后,才进行提交或者回滚。
|
||||
|
||||
## 存在的问题
|
||||
|
||||
### 1. 同步阻塞
|
||||
|
||||
所有事务参与者在等待其它参与者响应的时候都处于同步阻塞状态,无法进行其它操作。
|
||||
|
||||
### 2. 单点问题
|
||||
|
||||
协调者在 2PC 中起到非常大的作用,发生故障将会造成很大影响,特别是在阶段二发生故障,所有参与者会一直等待状态,无法完成其它操作。
|
||||
|
||||
### 3. 数据不一致
|
||||
|
||||
在阶段二,如果协调者只发送了部分 Commit 消息,此时网络发生异常,那么只有部分参与者接收到 Commit 消息,也就是说只有部分参与者提交了事务,使得系统数据不一致。
|
||||
|
||||
### 4. 太过保守
|
||||
|
||||
任意一个节点失败就会导致整个事务失败,没有完善的容错机制。
|
||||
|
||||
# 四、Paxos
|
||||
|
||||
用于达成共识性问题,即对多个节点产生的值,该算法能保证只选出唯一一个值。
|
||||
|
||||
主要有三类节点:
|
||||
|
||||
- 提议者(Proposer):提议一个值;
|
||||
- 接受者(Acceptor):对每个提议进行投票;
|
||||
- 告知者(Learner):被告知投票的结果,不参与投票过程。
|
||||
|
||||
<div align="center"> <img src="../pics//b988877c-0f0a-4593-916d-de2081320628.jpg" width=""/> </div><br>
|
||||
|
||||
## 执行过程
|
||||
|
||||
规定一个提议包含两个字段:[n, v],其中 n 为序号(具有唯一性),v 为提议值。
|
||||
|
||||
下图演示了两个 Proposer 和三个 Acceptor 的系统中运行该算法的初始过程,每个 Proposer 都会向所有 Acceptor 发送提议请求。
|
||||
|
||||
<div align="center"> <img src="../pics//1a9977e4-2f5c-49a6-aec9-f3027c9f46a7.png" width=""/> </div><br>
|
||||
|
||||
当 Acceptor 接收到一个提议请求,包含的提议为 [n1, v1],并且之前还未接收过提议请求,那么发送一个提议响应,设置当前接收到的提议为 [n1, v1],并且保证以后不会再接受序号小于 n1 的提议。
|
||||
|
||||
如下图,Acceptor X 在收到 [n=2, v=8] 的提议请求时,由于之前没有接收过提议,因此就发送一个 [no previous] 的提议响应,设置当前接收到的提议为 [n=2, v=8],并且保证以后不会再接受序号小于 2 的提议。其它的 Acceptor 类似。
|
||||
|
||||
<div align="center"> <img src="../pics//fb44307f-8e98-4ff7-a918-31dacfa564b4.jpg" width=""/> </div><br>
|
||||
|
||||
如果 Acceptor 接收到一个提议请求,包含的提议为 [n2, v2],并且之前已经接收过提议 [n1, v1]。如果 n1 > n2,那么就丢弃该提议请求;否则,发送提议响应,该提议响应包含之前已经接收过的提议 [n1, v1],设置当前接收到的提议为 [n2, v2],并且保证以后不会再接受序号小于 n2 的提议。
|
||||
|
||||
如下图,Acceptor Z 收到 Proposer A 发来的 [n=2, v=8] 的提议请求,由于之前已经接收过 [n=4, v=5] 的提议,并且 n > 2,因此就抛弃该提议请求;Acceptor X 收到 Proposer B 发来的 [n=4, v=5] 的提议请求,因为之前接收到的提议为 [n=2, v=8],并且 2 <= 4,因此就发送 [n=2, v=8] 的提议响应,设置当前接收到的提议为 [n=4, v=5],并且保证以后不会再接受序号小于 4 的提议。Acceptor Y 类似。
|
||||
|
||||
<div align="center"> <img src="../pics//2bcc58ad-bf7f-485c-89b5-e7cafc211ce2.jpg" width=""/> </div><br>
|
||||
|
||||
当一个 Proposer 接收到超过一半 Acceptor 的提议响应时,就可以发送接受请求。
|
||||
|
||||
Proposer A 接收到两个提议响应之后,就发送 [n=2, v=8] 接受请求。该接受请求会被所有 Acceptor 丢弃,因为此时所有 Acceptor 都保证不接受序号小于 4 的提议。
|
||||
|
||||
Proposer B 过后也收到了两个提议响应,因此也开始发送接受请求。需要注意的是,接受请求的 v 需要取它收到的最大 v 值,也就是 8。因此它发送 [n=4, v=8] 的接受请求。
|
||||
|
||||
<div align="center"> <img src="../pics//9b838aee-0996-44a5-9b0f-3d1e3e2f5100.png" width=""/> </div><br>
|
||||
|
||||
Acceptor 接收到接受请求时,如果序号大于等于该 Acceptor 承诺的最小序号,那么就发送通知给所有的 Learner。当 Learner 发现有大多数的 Acceptor 接收了某个提议,那么该提议的提议值就被 Paxos 选择出来。
|
||||
|
||||
<div align="center"> <img src="../pics//bf667594-bb4b-4634-bf9b-0596a45415ba.jpg" width=""/> </div><br>
|
||||
|
||||
## 约束条件
|
||||
|
||||
### 1. 正确性
|
||||
|
||||
指只有一个提议值会生效。
|
||||
|
||||
因为 Paxos 协议要求每个生效的提议被多数 Acceptor 接收,并且 Acceptor 不会接受两个不同的提议,因此可以保证正确性。
|
||||
|
||||
### 2. 可终止性
|
||||
|
||||
指最后总会有一个提议生效。
|
||||
|
||||
Paxos 协议能够让 Proposer 发送的提议朝着能被大多数 Acceptor 接受的那个提议靠拢,因此能够保证可终止性。
|
||||
|
||||
# 五、Raft
|
||||
|
||||
Raft 和 Paxos 类似,但是更容易理解,也更容易实现。
|
||||
|
||||
Raft 主要是用来竞选主节点。
|
||||
|
||||
## 单个 Candidate 的竞选
|
||||
|
||||
有三种节点:Follower、Candidate 和 Leader。Leader 会周期性的发送心跳包给 Follower。每个 Follower 都设置了一个随机的竞选超时时间,一般为 150ms\~300ms,如果在这个时间内没有收到 Leader 的心跳包,就会变成 Candidate,进入竞选阶段。
|
||||
|
||||
- 下图表示一个分布式系统的最初阶段,此时只有 Follower,没有 Leader。Follower A 等待一个随机的竞选超时时间之后,没收到 Leader 发来的心跳包,因此进入竞选阶段。
|
||||
|
||||
<div align="center"> <img src="../pics//111521118015898.gif" width=""/> </div><br>
|
||||
|
||||
- 此时 A 发送投票请求给其它所有节点。
|
||||
|
||||
<div align="center"> <img src="../pics//111521118445538.gif" width=""/> </div><br>
|
||||
|
||||
- 其它节点会对请求进行回复,如果超过一半的节点回复了,那么该 Candidate 就会变成 Leader。
|
||||
|
||||
<div align="center"> <img src="../pics//111521118483039.gif" width=""/> </div><br>
|
||||
|
||||
- 之后 Leader 会周期性地发送心跳包给 Follower,Follower 接收到心跳包,会重新开始计时。
|
||||
|
||||
<div align="center"> <img src="../pics//111521118640738.gif" width=""/> </div><br>
|
||||
|
||||
## 多个 Candidate 竞选
|
||||
|
||||
* 如果有多个 Follower 成为 Candidate,并且所获得票数相同,那么就需要重新开始投票,例如下图中 Candidate B 和 Candidate D 都获得两票,因此需要重新开始投票。
|
||||
|
||||
<div align="center"> <img src="../pics//111521119203347.gif" width=""/> </div><br>
|
||||
|
||||
* 当重新开始投票时,由于每个节点设置的随机竞选超时时间不同,因此能下一次再次出现多个 Candidate 并获得同样票数的概率很低。
|
||||
|
||||
<div align="center"> <img src="../pics//111521119368714.gif" width=""/> </div><br>
|
||||
|
||||
## 日志复制
|
||||
|
||||
- 来自客户端的修改都会被传入 Leader。注意该修改还未被提交,只是写入日志中。
|
||||
|
||||
<div align="center"> <img src="../pics//7.gif" width=""/> </div><br>
|
||||
|
||||
- Leader 会把修改复制到所有 Follower。
|
||||
|
||||
<div align="center"> <img src="../pics//9.gif" width=""/> </div><br>
|
||||
|
||||
- Leader 会等待大多数的 Follower 也进行了修改,然后才将修改提交。
|
||||
|
||||
<div align="center"> <img src="../pics//10.gif" width=""/> </div><br>
|
||||
|
||||
- 此时 Leader 会通知的所有 Follower 让它们也提交修改,此时所有节点的值达成一致。
|
||||
|
||||
<div align="center"> <img src="../pics//11.gif" width=""/> </div><br>
|
||||
|
||||
# 参考资料
|
||||
|
||||
- 倪超. 从 Paxos 到 ZooKeeper : 分布式一致性原理与实践 [M]. 电子工业出版社, 2015.
|
||||
- [What is CAP theorem in distributed database system?](http://www.colooshiki.com/index.php/2017/04/20/what-is-cap-theorem-in-distributed-database-system/)
|
||||
- [NEAT ALGORITHMS - PAXOS](http://harry.me/blog/2014/12/27/neat-algorithms-paxos/)
|
||||
- [Raft: Understandable Distributed Consensus](http://thesecretlivesofdata.com/raft)
|
||||
- [Paxos By Example](https://angus.nyc/2012/paxos-by-example/)
|
||||
|
342
notes/分布式基础.md
@ -1,342 +0,0 @@
|
||||
<!-- GFM-TOC -->
|
||||
* [一、基本概念](#一基本概念)
|
||||
* [异常](#异常)
|
||||
* [超时](#超时)
|
||||
* [衡量指标](#衡量指标)
|
||||
* [二、数据分布](#二数据分布)
|
||||
* [哈希分布](#哈希分布)
|
||||
* [顺序分布](#顺序分布)
|
||||
* [负载均衡](#负载均衡)
|
||||
* [三、复制](#三复制)
|
||||
* [复制原理](#复制原理)
|
||||
* [复制协议](#复制协议)
|
||||
* [CAP](#cap)
|
||||
* [BASE](#base)
|
||||
* [四、容错](#四容错)
|
||||
* [故障检测](#故障检测)
|
||||
* [故障恢复](#故障恢复)
|
||||
* [五、一致性协议](#五一致性协议)
|
||||
* [Paxos 协议](#paxos-协议)
|
||||
* [Raft 协议](#raft-协议)
|
||||
* [拜占庭将军问题](#拜占庭将军问题)
|
||||
* [六、CDN 架构](#六cdn-架构)
|
||||
* [参考资料](#参考资料)
|
||||
<!-- GFM-TOC -->
|
||||
|
||||
|
||||
# 一、基本概念
|
||||
|
||||
## 异常
|
||||
|
||||
### 1. 服务器宕机
|
||||
|
||||
内存错误、服务器停电等都会导致服务器宕机,此时节点无法正常工作,称为不可用。
|
||||
|
||||
服务器宕机会导致节点失去所有内存信息,因此需要将内存信息保存到持久化介质上。
|
||||
|
||||
### 2. 网络异常
|
||||
|
||||
有一种特殊的网络异常称为 **网络分区** ,即集群的所有节点被划分为多个区域,每个区域内部可以通信,但是区域之间无法通信。
|
||||
|
||||
### 3. 磁盘故障
|
||||
|
||||
磁盘故障是一种发生概率很高的异常。
|
||||
|
||||
使用冗余机制,将数据存储到多台服务器。
|
||||
|
||||
## 超时
|
||||
|
||||
在分布式系统中,一个请求除了成功和失败两种状态,还存在着超时状态。
|
||||
|
||||
<div align="center"> <img src="../pics//b0e8ef47-2f23-4379-8c64-10d5cb44d438.jpg"/> </div><br>
|
||||
|
||||
可以将服务器的操作设计为具有 **幂等性** ,即执行多次的结果与执行一次的结果相同。如果使用这种方式,当出现超时的时候,可以不断地重新请求直到成功。
|
||||
|
||||
## 衡量指标
|
||||
|
||||
### 1. 性能
|
||||
|
||||
常见的性能指标有:吞吐量、响应时间。
|
||||
|
||||
其中,吞吐量指系统在某一段时间可以处理的请求总数,通常为每秒的读操作数或者写操作数;响应时间指从某个请求发出到接收到返回结果消耗的时间。
|
||||
|
||||
这两个指标往往是矛盾的,追求高吞吐的系统,往往很难做到低响应时间,解释如下:
|
||||
|
||||
- 在无并发的系统中,吞吐量为响应时间的倒数,例如响应时间为 10 ms,那么吞吐量为 100 req/s,因此高吞吐也就意味着低响应时间。
|
||||
|
||||
- 但是在并发的系统中,由于一个请求在调用 I/O 资源的时候,需要进行等待。服务器端一般使用的是异步等待方式,即等待的请求被阻塞之后不需要一直占用 CPU 资源。这种方式能大大提高 CPU 资源的利用率,例如上面的例子中,单个请求在无并发的系统中响应时间为 10 ms,如果在并发的系统中,那么吞吐量将大于 100 req/s。因此为了追求高吞吐量,通常会提高并发程度。但是并发程度的增加,会导致请求的平均响应时间也增加,因为请求不能马上被处理,需要和其它请求一起进行并发处理,响应时间自然就会增高。
|
||||
|
||||
### 2. 可用性
|
||||
|
||||
可用性指系统在面对各种异常时可以提供正常服务的能力。可以用系统可用时间占总时间的比值来衡量,4 个 9 的可用性表示系统 99.99% 的时间是可用的。
|
||||
|
||||
### 3. 一致性
|
||||
|
||||
可以从两个角度理解一致性:从客户端的角度,读写操作是否满足某种特性;从服务器的角度,多个数据副本之间是否一致。
|
||||
|
||||
### 4. 可扩展性
|
||||
|
||||
指系统通过扩展集群服务器规模来提高性能的能力。理想的分布式系统需要实现“线性可扩展”,即随着集群规模的增加,系统的整体性能也会线性增加。
|
||||
|
||||
# 二、数据分布
|
||||
|
||||
分布式存储系统的数据分布在多个节点中,常用的数据分布方式有哈希分布和顺序分布。
|
||||
|
||||
数据库的水平切分(Sharding)也是一种分布式存储方法,下面的数据分布方法同样适用于 Sharding。
|
||||
|
||||
## 哈希分布
|
||||
|
||||
哈希分布就是将数据计算哈希值之后,按照哈希值分配到不同的节点上。例如有 N 个节点,数据的主键为 key,则将该数据分配的节点序号为:hash(key)%N。
|
||||
|
||||
传统的哈希分布算法存在一个问题:当节点数量变化时,也就是 N 值变化,那么几乎所有的数据都需要重新分布,将导致大量的数据迁移。
|
||||
|
||||
**一致性哈希**
|
||||
|
||||
Distributed Hash Table(DHT):对于哈希空间 [0, 2<sup>n</sup>-1],将该哈希空间看成一个哈希环,将每个节点都配置到哈希环上。每个数据对象通过哈希取模得到哈希值之后,存放到哈希环中顺时针方向第一个大于等于该哈希值的节点上。
|
||||
|
||||
<div align="center"> <img src="../pics//d2d34239-e7c1-482b-b33e-3170c5943556.jpg"/> </div><br>
|
||||
|
||||
一致性哈希的优点是在增加或者删除节点时只会影响到哈希环中相邻的节点,例如下图中新增节点 X,只需要将数据对象 C 重新存放到节点 X 上即可,对于节点 A、B、D 都没有影响。
|
||||
|
||||
<div align="center"> <img src="../pics//91ef04e4-923a-4277-99c0-6be4ce81e5ac.jpg"/> </div><br>
|
||||
|
||||
## 顺序分布
|
||||
|
||||
哈希分布式破坏了数据的有序性,顺序分布则不会。
|
||||
|
||||
顺序分布的数据划分为多个连续的部分,按数据的 ID 或者时间分布到不同节点上。例如下图中,User 表的 ID 范围为 1 \~ 7000,使用顺序分布可以将其划分成多个子表,对应的主键范围为 1 \~ 1000,1001 \~ 2000,...,6001 \~ 7000。
|
||||
|
||||
顺序分布的优点是可以充分利用每个节点的空间,而哈希分布很难控制一个节点存储多少数据。
|
||||
|
||||
但是顺序分布需要使用一个映射表来存储数据到节点的映射,这个映射表通常使用单独的节点来存储。当数据量非常大时,映射表也随着变大,那么一个节点就可能无法存放下整个映射表。并且单个节点维护着整个映射表的开销很大,查找速度也会变慢。为了解决以上问题,引入了一个中间层,也就是 Meta 表,从而分担映射表的维护工作。
|
||||
|
||||
<div align="center"> <img src="../pics//8f64e9c5-7682-4feb-9312-dea09514e160.jpg"/> </div><br>
|
||||
|
||||
## 负载均衡
|
||||
|
||||
衡量负载的因素很多,如 CPU、内存、磁盘等资源使用情况、读写请求数等。
|
||||
|
||||
分布式系统存储应当能够自动负载均衡,当某个节点的负载较高,将它的部分数据迁移到其它节点。
|
||||
|
||||
每个集群都有一个总控节点,其它节点为工作节点,由总控节点根据全局负载信息进行整体调度,工作节点定时发送心跳包(Heartbeat)将节点负载相关的信息发送给总控节点。
|
||||
|
||||
一个新上线的工作节点,由于其负载较低,如果不加控制,总控节点会将大量数据同时迁移到该节点上,造成该节点一段时间内无法工作。因此负载均衡操作需要平滑进行,新加入的节点需要较长的一段时间来达到比较均衡的状态。
|
||||
|
||||
# 三、复制
|
||||
|
||||
## 复制原理
|
||||
|
||||
复制是保证分布式系统高可用的基础,让一个数据存储多个副本,当某个副本所在的节点出现故障时,能够自动切换到其它副本上,从而实现故障恢复。
|
||||
|
||||
多个副本通常有一个为主副本,其它为备副本。主副本用来处理写请求,备副本主要用来处理读请求,实现读写分离。
|
||||
|
||||
主副本将同步操作日志发送给备副本,备副本通过回放操作日志获取最新修改。
|
||||
|
||||
<div align="center"> <img src="../pics//44e4a7ab-215c-41a1-8e34-f55f6c09e517.jpg"/> </div><br>
|
||||
|
||||
## 复制协议
|
||||
|
||||
主备副本之间有两种复制协议,一种是强同步复制协议,一种是异步复制协议。
|
||||
|
||||
### 1. 强同步复制协议
|
||||
|
||||
要求主副本将同步操作日志发给备副本之后进行等待,要求至少一个备副本返回成功后,才开始修改主副本,修改完成之后通知客户端操作成功。
|
||||
|
||||
优点:至少有一个备副本拥有完整的数据,出现故障时可以安全地切换到该备副本,因此一致性好。
|
||||
|
||||
缺点:可用性差,因为主副本需要等待,那么整个分布式系统的可用时间就会降低。
|
||||
|
||||
### 2. 异步复制协议
|
||||
|
||||
主副本将同步操作日志发给备副本之后不需要进行等待,直接修改主副本并通知客户端操作成功。
|
||||
|
||||
优点:可用性好。
|
||||
|
||||
缺点:一致性差。
|
||||
|
||||
## CAP
|
||||
|
||||
分布式存储系统不可能同时满足一致性(C:Consistency)、可用性(A:Availability)和分区容忍性(P:Partition tolerance),最多只能同时满足其中两项。
|
||||
|
||||
在设计分布式系统时,需要根据实际需求弱化某一要求。因此就有了下图中的三种设计:CA、CP 和 AP。
|
||||
|
||||
需要注意的是,分区容忍性必不可少,因为需要总是假设网络是不可靠的,并且系统需要能够自动容错,因此实际上设计分布式存储系统需要在一致性和可用性之间做权衡。上一节介绍的强同步协议和异步复制协议就是在一致性和可用性做权衡得到的结果。
|
||||
|
||||
<div align="center"> <img src="../pics//992faced-afcf-414d-b801-9c16d6570fec.jpg" width="500"/> </div><br>
|
||||
|
||||
## BASE
|
||||
|
||||
BASE 是 Basically Available(基本可用)、Soft State(软状态)和 Eventually Consistent(最终一致性)三个短语的缩写。BASE 理论是对 CAP 中一致性和可用性权衡的结果,是基于 CAP 定理逐步演化而来的。BASE 理论的核心思想是:即使无法做到强一致性,但每个应用都可以根据自身业务特点,采用适当的方式来使系统达到最终一致性。
|
||||
|
||||
<div align="center"> <img src="../pics//5930aeb8-847d-4e9f-a168-9334d7dec744.png" width="250"/> </div><br>
|
||||
|
||||
### 1. 基本可用
|
||||
|
||||
指分布式系统在出现故障的时候,保证核心可用,允许损失部分可用性。
|
||||
|
||||
例如,电商在做促销时,服务层可能只提供降级服务,部分用户可能会被引导到降级页面上。
|
||||
|
||||
### 2. 软状态
|
||||
|
||||
指允许系统存在中间状态,而该中间状态不会影响系统整体可用性,即不同节点的数据副本之间进行同步的过程允许存在延时。
|
||||
|
||||
### 3. 最终一致性
|
||||
|
||||
一致性模型包含以下三种:
|
||||
|
||||
- 强一致性:新数据写入之后,在任何数据副本上都能读取到最新值;
|
||||
- 弱一致性:新数据写入之后,不能保证在数据副本上能读取到最新值;
|
||||
- 最终一致性:新数据写入之后,只能保证过了一个时间窗口后才能在数据副本上读取到最新值;
|
||||
|
||||
强一致性通常运用在需要满足 ACID 的传统数据库系统上,而最终一致性通常运用在大型分布式系统中。应该注意的是,上面介绍的强同步复制协议和异步复制协议都不能保证强一致性,因为它们是分布式系统的复制协议。这两种复制协议如果要满足最终一致性,还需要多加一些控制。
|
||||
|
||||
在实际的分布式场景中,不同业务单元和组件对一致性的要求是不同的,因此 ACID 和 BASE 往往会结合在一起使用。
|
||||
|
||||
# 四、容错
|
||||
|
||||
分布式系统故障发生的概率很大,为了实现高可用以及减少人工运维成本,需要实现自动化容错。
|
||||
|
||||
## 故障检测
|
||||
|
||||
通过 **租约机制** 来对故障进行检测。假设节点 A 为主控节点,节点 A 向节点 B 发送租约,节点 B 在租约规定的期限内才能提供服务。期限快到达时,节点 B 需要向 A 重新申请租约。
|
||||
|
||||
如果过期,那么 B 不再提供服务,并且 A 也能知道 B 此时可能发生故障并已经停止服务。可以看到,通过这种机制,A 和 B 都能对 B 发生故障这一事实达成一致。
|
||||
|
||||
## 故障恢复
|
||||
|
||||
当某个节点故障时,就将它上面的服务迁移到其它节点。
|
||||
|
||||
# 五、一致性协议
|
||||
|
||||
## Paxos 协议
|
||||
|
||||
用于达成共识性问题,即对多个节点产生的值,该算法能保证只选出唯一一个值。
|
||||
|
||||
主要有三类节点:
|
||||
|
||||
- 提议者(Proposer):提议一个值;
|
||||
- 接受者(Acceptor):对每个提议进行投票;
|
||||
- 告知者(Learner):被告知投票的结果,不参与投票过程。
|
||||
|
||||
<div align="center"> <img src="../pics//0aaf4630-d2a2-4783-b3f7-a2b6a7dfc01b.jpg"/> </div><br>
|
||||
|
||||
### 1. 执行过程
|
||||
|
||||
规定一个提议包含两个字段:[n, v],其中 n 为序号(具有唯一性),v 为提议值。
|
||||
|
||||
下图演示了两个 Proposer 和三个 Acceptor 的系统中运行该算法的初始过程,每个 Proposer 都会向所有 Acceptor 发送提议请求。
|
||||
|
||||
<div align="center"> <img src="../pics//2bf2fd8f-5ade-48ba-a2b3-74195ac77c4b.png"/> </div><br>
|
||||
|
||||
当 Acceptor 接收到一个提议请求,包含的提议为 [n1, v1],并且之前还未接收过提议请求,那么发送一个提议响应,设置当前接收到的提议为 [n1, v1],并且保证以后不会再接受序号小于 n1 的提议。
|
||||
|
||||
如下图,Acceptor X 在收到 [n=2, v=8] 的提议请求时,由于之前没有接收过提议,因此就发送一个 [no previous] 的提议响应,设置当前接收到的提议为 [n=2, v=8],并且保证以后不会再接受序号小于 2 的提议。其它的 Acceptor 类似。
|
||||
|
||||
<div align="center"> <img src="../pics//3f5bba4b-7813-4aea-b578-970c7e3f6bf3.jpg"/> </div><br>
|
||||
|
||||
如果 Acceptor 接收到一个提议请求,包含的提议为 [n2, v2],并且之前已经接收过提议 [n1, v1]。如果 n1 > n2,那么就丢弃该提议请求;否则,发送提议响应,该提议响应包含之前已经接收过的提议 [n1, v1],设置当前接收到的提议为 [n2, v2],并且保证以后不会再接受序号小于 n2 的提议。
|
||||
|
||||
如下图,Acceptor Z 收到 Proposer A 发来的 [n=2, v=8] 的提议请求,由于之前已经接收过 [n=4, v=5] 的提议,并且 n > 2,因此就抛弃该提议请求;Acceptor X 收到 Proposer B 发来的 [n=4, v=5] 的提议请求,因为之前接收到的提议为 [n=2, v=8],并且 2 <= 4,因此就发送 [n=2, v=8] 的提议响应,设置当前接收到的提议为 [n=4, v=5],并且保证以后不会再接受序号小于 4 的提议。Acceptor Y 类似。
|
||||
|
||||
<div align="center"> <img src="../pics//9b829410-86c4-40aa-ba8d-9e8e26c0eeb8.jpg"/> </div><br>
|
||||
|
||||
当一个 Proposer 接收到超过一半 Acceptor 的提议响应时,就可以发送接受请求。
|
||||
|
||||
Proposer A 接收到两个提议响应之后,就发送 [n=2, v=8] 接受请求。该接受请求会被所有 Acceptor 丢弃,因为此时所有 Acceptor 都保证不接受序号小于 4 的提议。
|
||||
|
||||
Proposer B 过后也收到了两个提议响应,因此也开始发送接受请求。需要注意的是,接受请求的 v 需要取它收到的最大 v 值,也就是 8。因此它发送 [n=4, v=8] 的接受请求。
|
||||
|
||||
<div align="center"> <img src="../pics//2c4556e4-0751-4377-ab08-e7b89d697ca7.png"/> </div><br>
|
||||
|
||||
Acceptor 接收到接受请求时,如果序号大于等于该 Acceptor 承诺的最小序号,那么就发送通知给所有的 Learner。当 Learner 发现有大多数的 Acceptor 接收了某个提议,那么该提议的提议值就被 Paxos 选择出来。
|
||||
|
||||
<div align="center"> <img src="../pics//8adb2591-d3f1-4632-84cb-823fb9c5eb09.jpg"/> </div><br>
|
||||
|
||||
### 2. 约束条件
|
||||
|
||||
**(一)正确性**
|
||||
|
||||
指只有一个提议值会生效。
|
||||
|
||||
因为 Paxos 协议要求每个生效的提议被多数 Acceptor 接收,并且 Acceptor 不会接受两个不同的提议,因此可以保证正确性。
|
||||
|
||||
**(二)可终止性**
|
||||
|
||||
指最后总会有一个提议生效。
|
||||
|
||||
Paxos 协议能够让 Proposer 发送的提议朝着能被大多数 Acceptor 接受的那个提议靠拢,因此能够保证可终止性。
|
||||
|
||||
## Raft 协议
|
||||
|
||||
Raft 和 Paxos 类似,但是更容易理解,也更容易实现。
|
||||
|
||||
Raft 主要是用来竞选主节点。
|
||||
|
||||
### 1. 单个 Candidate 的竞选
|
||||
|
||||
有三种节点:Follower、Candidate 和 Leader。Leader 会周期性的发送心跳包给 Follower。每个 Follower 都设置了一个随机的竞选超时时间,一般为 150ms\~300ms,如果在这个时间内没有收到 Leader 的心跳包,就会变成 Candidate,进入竞选阶段。
|
||||
|
||||
- 下图表示一个分布式系统的最初阶段,此时只有 Follower,没有 Leader。Follower A 等待一个随机的竞选超时时间之后,没收到 Leader 发来的心跳包,因此进入竞选阶段。
|
||||
|
||||
<div align="center"> <img src="../pics//111521118015898.gif"/> </div><br>
|
||||
|
||||
- 此时 A 发送投票请求给其它所有节点。
|
||||
|
||||
<div align="center"> <img src="../pics//111521118445538.gif"/> </div><br>
|
||||
|
||||
- 其它节点会对请求进行回复,如果超过一半的节点回复了,那么该 Candidate 就会变成 Leader。
|
||||
|
||||
<div align="center"> <img src="../pics//111521118483039.gif"/> </div><br>
|
||||
|
||||
- 之后 Leader 会周期性地发送心跳包给 Follower,Follower 接收到心跳包,会重新开始计时。
|
||||
|
||||
<div align="center"> <img src="../pics//111521118640738.gif"/> </div><br>
|
||||
|
||||
### 2. 多个 Candidate 竞选
|
||||
|
||||
* 如果有多个 Follower 成为 Candidate,并且所获得票数相同,那么就需要重新开始投票,例如下图中 Candidate B 和 Candidate D 都获得两票,因此需要重新开始投票。
|
||||
|
||||
<div align="center"> <img src="../pics//111521119203347.gif"/> </div><br>
|
||||
|
||||
* 当重新开始投票时,由于每个节点设置的随机竞选超时时间不同,因此能下一次再次出现多个 Candidate 并获得同样票数的概率很低。
|
||||
|
||||
<div align="center"> <img src="../pics//111521119368714.gif"/> </div><br>
|
||||
|
||||
### 3. 日志复制
|
||||
|
||||
- 来自客户端的修改都会被传入 Leader。注意该修改还未被提交,只是写入日志中。
|
||||
|
||||
<div align="center"> <img src="../pics//7.gif"/> </div><br>
|
||||
|
||||
- Leader 会把修改复制到所有 Follower。
|
||||
|
||||
<div align="center"> <img src="../pics//9.gif"/> </div><br>
|
||||
|
||||
- Leader 会等待大多数的 Follower 也进行了修改,然后才将修改提交。
|
||||
|
||||
<div align="center"> <img src="../pics//10.gif"/> </div><br>
|
||||
|
||||
- 此时 Leader 会通知的所有 Follower 让它们也提交修改,此时所有节点的值达成一致。
|
||||
|
||||
<div align="center"> <img src="../pics//11.gif"/> </div><br>
|
||||
|
||||
## 拜占庭将军问题
|
||||
|
||||
> [拜占庭将军问题深入探讨](http://www.8btc.com/baizhantingjiangjun)
|
||||
|
||||
# 六、CDN 架构
|
||||
|
||||
通过将内容发布到靠近用户的边缘节点,使不同地域的用户在访问相同网页时可以就近获取。不仅可以减轻服务器的负担,也可以提高用户的访问速度。
|
||||
|
||||
从下图可以看出,DNS 在对域名解析时不再向用户返回源服务器的 IP 地址,而是返回边缘节点的 IP 地址,所以用户最终访问的是边缘节点。边缘节点会先从源服务器中获取用户所需的数据,如果请求成功,边缘节点会将页面缓存下来,下次用户访问时可以直接读取。
|
||||
|
||||
<div align="center"> <img src="../pics//dbd60b1f-b700-4da6-a993-62578e892333.jpg"/> </div><br>
|
||||
|
||||
# 参考资料
|
||||
|
||||
- 杨传辉. 大规模分布式存储系统: 原理解析与架构实战[M]. 机械工业出版社, 2013.
|
||||
- 杨传辉. 大规模分布式存储系统: 原理解析与架构实战[M]. 机械工业出版社, 2013.
|
||||
- [区块链技术指南](https://www.gitbook.com/book/yeasy/blockchain_guide/details)
|
||||
- [NEAT ALGORITHMS - PAXOS](http://harry.me/blog/2014/12/27/neat-algorithms-paxos/)
|
||||
- [Raft: Understandable Distributed Consensus](http://thesecretlivesofdata.com/raft)
|
||||
- [Paxos By Example](https://angus.nyc/2012/paxos-by-example/)
|
285
notes/分布式问题分析.md
@ -1,285 +0,0 @@
|
||||
<!-- GFM-TOC -->
|
||||
* [一、分布式事务](#一分布式事务)
|
||||
* [两阶段提交协议](#两阶段提交协议)
|
||||
* [本地消息](#本地消息)
|
||||
* [二、分布式锁](#二分布式锁)
|
||||
* [原理](#原理)
|
||||
* [实现](#实现)
|
||||
* [三、分布式 Session](#三分布式-session)
|
||||
* [四、负载均衡](#四负载均衡)
|
||||
* [算法](#算法)
|
||||
* [实现](#实现)
|
||||
* [参考资料](#参考资料)
|
||||
<!-- GFM-TOC -->
|
||||
|
||||
|
||||
# 一、分布式事务
|
||||
|
||||
指事务的操作位于不同的节点上,需要保证事务的 AICD 特性。例如在下单场景下,库存和订单如果不在同一个节点上,就需要涉及分布式事务。
|
||||
|
||||
## 两阶段提交协议
|
||||
|
||||
Two-phase Commit(2PC)。
|
||||
|
||||
两类节点:协调者(Coordinator)和参与者(Participants),协调者只有一个,参与者可以有多个。
|
||||
|
||||
### 1. 运行过程
|
||||
|
||||
① 准备阶段:协调者询问参与者事务是否执行成功;
|
||||
|
||||
<div align="center"> <img src="../pics//c8dbff58-d981-48be-8c1c-caa6c2738791.jpg"/> </div><br>
|
||||
|
||||
② 提交阶段:如果事务在每个参与者上都执行成功,协调者发送通知让参与者提交事务;否则,协调者发送通知让参与者回滚事务。
|
||||
|
||||
<div align="center"> <img src="../pics//aa844ff0-cd16-4478-b415-da071b615a17.jpg"/> </div><br>
|
||||
|
||||
需要注意的是,在准备阶段,参与者执行了事务,但是还未提交。只有在提交阶段接收到协调者发来的通知后,才进行提交或者回滚。
|
||||
|
||||
### 2. 分析
|
||||
|
||||
2PC 可以保证强一致性,但是因为在准备阶段协调者需要等待所有参与者的结果才能进入提交阶段,因此可用性差。
|
||||
|
||||
### 3. 存在的问题
|
||||
|
||||
- 参与者发生故障。解决方案:可以给事务设置一个超时时间,如果某个参与者一直不响应,那么认为事务执行失败。
|
||||
- 协调者发生故障。解决方案:将操作日志同步到备用协调者,让备用协调者接替后续工作。
|
||||
|
||||
### 4. XA 协议
|
||||
|
||||
XA 协议是多数数据库的 2PC 协议的实现,包含了事务管理器和本地资源管理器。
|
||||
|
||||
## 本地消息
|
||||
|
||||
### 1. 原理
|
||||
|
||||
本地消息表与业务数据表处于同一个数据库中,这样就能利用本地事务来保证在对这两个表的操作满足事务特性。
|
||||
|
||||
1. 在分布式事务操作的一方,它完成写业务数据的操作之后向本地消息表发送一个消息,本地事务能保证这个消息一定会被写入本地消息表中。
|
||||
2. 之后将本地消息表中的消息转发到 Kafka 等消息队列(MQ)中,如果转发成功则将消息从本地消息表中删除,否则继续重新转发。
|
||||
3. 在分布式事务操作的另一方从消息队列中读取一个消息,并执行消息中的操作。
|
||||
|
||||
<div align="center"> <img src="../pics//e3bf5de4-ab1e-4a9b-896d-4b0ad7e9220a.jpg"/> </div><br>
|
||||
|
||||
### 2. 分析
|
||||
|
||||
本地消息表利用了本地事务来实现分布式事务,并且使用了消息队列来保证最终一致性。
|
||||
|
||||
# 二、分布式锁
|
||||
|
||||
可以使用 Java 提供的内置锁来实现进程同步:由 JVM 实现的 synchronized 和 JDK 提供的 Lock。但是在分布式场景下,需要同步的进程可能位于不同的节点上,那么就需要使用分布式锁来同步。
|
||||
|
||||
## 原理
|
||||
|
||||
锁可以有阻塞锁和乐观锁两种实现方式,这里主要探讨阻塞锁实现。阻塞锁通常使用互斥量来实现,互斥量为 1 表示有其它进程在使用锁,此时处于锁定状态,互斥量为 0 表示未锁定状态。1 和 0 可以用一个整型值来存储,也可以用某个数据存在或者不存在来存储,某个数据存在表示互斥量为 1,也就是锁定状态。
|
||||
|
||||
## 实现
|
||||
|
||||
### 1. 数据库的唯一索引
|
||||
|
||||
当想要获得锁时,就向表中插入一条记录,释放锁时就删除这条记录。唯一索引可以保证该记录只被插入一次,那么就可以用这个记录是否存在来判断是否存于锁定状态。
|
||||
|
||||
这种方式存在以下几个问题:
|
||||
|
||||
- 锁没有失效时间,解锁失败会导致死锁,其他线程无法再获得锁。
|
||||
- 只能是非阻塞锁,插入失败直接就报错了,无法重试。
|
||||
- 不可重入,同一线程在没有释放锁之前无法再获得锁。
|
||||
|
||||
### 2. Redis 的 SETNX 指令
|
||||
|
||||
使用 SETNX(set if not exist)指令插入一个键值对,如果 Key 已经存在,那么会返回 False,否则插入成功并返回 True。
|
||||
|
||||
SETNX 指令和数据库的唯一索引类似,可以保证只存在一个 Key 的键值对,可以用一个 Key 的键值对是否存在来判断是否存于锁定状态。
|
||||
|
||||
EXPIRE 指令可以为一个键值对设置一个过期时间,从而避免了死锁的发生。
|
||||
|
||||
### 3. Redis 的 RedLock 算法
|
||||
|
||||
使用了多个 Redis 实例来实现分布式锁,这是为了保证在发生单点故障时仍然可用。
|
||||
|
||||
- 尝试从 N 个相互独立 Redis 实例获取锁,如果一个实例不可用,应该尽快尝试下一个。
|
||||
- 计算获取锁消耗的时间,只有当这个时间小于锁的过期时间,并且从大多数(N/2+1)实例上获取了锁,那么就认为锁获取成功了。
|
||||
- 如果锁获取失败,会到每个实例上释放锁。
|
||||
|
||||
### 4. Zookeeper 的有序节点
|
||||
|
||||
Zookeeper 是一个为分布式应用提供一致性服务的软件,例如配置管理、分布式协同以及命名的中心化等,这些都是分布式系统中非常底层而且是必不可少的基本功能,但是如果自己实现这些功能而且要达到高吞吐、低延迟同时还要保持一致性和可用性,实际上非常困难。
|
||||
|
||||
**(一)抽象模型**
|
||||
|
||||
Zookeeper 提供了一种树形结构级的命名空间,/app1/p_1 节点表示它的父节点为 /app1。
|
||||
|
||||
<div align="center"> <img src="../pics//31d99967-1171-448e-8531-bccf5c14cffe.jpg" width="400"/> </div><br>
|
||||
|
||||
**(二)节点类型**
|
||||
|
||||
- 永久节点:不会因为会话结束或者超时而消失;
|
||||
- 临时节点:如果会话结束或者超时就会消失;
|
||||
- 有序节点:会在节点名的后面加一个数字后缀,并且是有序的,例如生成的有序节点为 /lock/node-0000000000,它的下一个有序节点则为 /lock/node-0000000001,依次类推。
|
||||
|
||||
**(三)监听器**
|
||||
|
||||
为一个节点注册监听器,在节点状态发生改变时,会给客户端发送消息。
|
||||
|
||||
**(四)分布式锁实现**
|
||||
|
||||
- 创建一个锁目录 /lock;
|
||||
- 在 /lock 下创建临时的且有序的子节点,第一个客户端对应的子节点为 /lock/lock-0000000000,第二个为 /lock/lock-0000000001,以此类推;
|
||||
- 客户端获取 /lock 下的子节点列表,判断自己创建的子节点是否为当前子节点列表中序号最小的子节点,如果是则认为获得锁;否则监听自己的前一个子节点,获得子节点的变更通知后重复此步骤直至获得锁;
|
||||
- 执行业务代码,完成后,删除对应的子节点。
|
||||
|
||||
**(五)会话超时**
|
||||
|
||||
如果一个已经获得锁的会话超时了,因为创建的是临时节点,所以该会话对应的临时节点会被删除,其它会话就可以获得锁了。可以看到,Zookeeper 分布式锁不会出现数据库的唯一索引实现分布式锁的死锁问题。
|
||||
|
||||
**(六)羊群效应**
|
||||
|
||||
一个节点未获得锁,需要监听自己的前一个子节点,这是因为如果监听所有的子节点,那么任意一个子节点状态改变,其它所有子节点都会收到通知(羊群效应),而我们只希望它的后一个子节点收到通知。
|
||||
|
||||
# 三、分布式 Session
|
||||
|
||||
在分布式场景下,一个用户的 Session 如果只存储在一个服务器上,那么当负载均衡器把用户的下一个请求转发到另一个服务器上,该服务器没有用户的 Session,就可能导致用户需要重新进行登录等操作。
|
||||
|
||||
<div align="center"> <img src="../pics//cookiedata.png"/> </div><br>
|
||||
|
||||
### 1. Sticky Sessions
|
||||
|
||||
需要配置负载均衡器,使得一个用户的所有请求都路由到一个服务器节点上,这样就可以把用户的 Session 存放在该服务器节点中。
|
||||
|
||||
缺点:当服务器节点宕机时,将丢失该服务器节点上的所有 Session。
|
||||
|
||||
<div align="center"> <img src="../pics//MultiNode-StickySessions.jpg"/> </div><br>
|
||||
|
||||
### 2. Session Replication
|
||||
|
||||
在服务器节点之间进行 Session 同步操作,这样的话用户可以访问任何一个服务器节点。
|
||||
|
||||
缺点:需要更好的服务器硬件条件;需要对服务器进行配置。
|
||||
|
||||
<div align="center"> <img src="../pics//MultiNode-SessionReplication.jpg"/> </div><br>
|
||||
|
||||
### 3. Persistent DataStore
|
||||
|
||||
将 Session 信息持久化到一个数据库中。
|
||||
|
||||
缺点:有可能需要去实现存取 Session 的代码。
|
||||
|
||||
<div align="center"> <img src="../pics//MultiNode-SpringSession.jpg"/> </div><br>
|
||||
|
||||
### 4. In-Memory DataStore
|
||||
|
||||
可以使用 Redis 和 Memcached 这种内存型数据库对 Session 进行存储,可以大大提高 Session 的读写效率。内存型数据库同样可以持久化数据到磁盘中来保证数据的安全性。
|
||||
|
||||
# 四、负载均衡
|
||||
|
||||
## 算法
|
||||
|
||||
### 1. 轮询(Round Robin)
|
||||
|
||||
轮询算法把每个请求轮流发送到每个服务器上。下图中,一共有 6 个客户端产生了 6 个请求,这 6 个请求按 (1, 2, 3, 4, 5, 6) 的顺序发送。最后,(1, 3, 5) 的请求会被发送到服务器 1,(2, 4, 6) 的请求会被发送到服务器 2。
|
||||
|
||||
<div align="center"> <img src="../pics//2766d04f-7dad-42e4-99d1-60682c9d5c61.jpg"/> </div><br>
|
||||
|
||||
该算法比较适合每个服务器的性能差不多的场景,如果有性能存在差异的情况下,那么性能较差的服务器可能无法承担过大的负载(下图的 Server 2)。
|
||||
|
||||
<div align="center"> <img src="../pics//f7ecbb8d-bb8b-4d45-a3b7-f49425d6d83d.jpg"/> </div><br>
|
||||
|
||||
### 2. 加权轮询(Weighted Round Robbin)
|
||||
|
||||
加权轮询是在轮询的基础上,根据服务器的性能差异,为服务器赋予一定的权值。例如下图中,服务器 1 被赋予的权值为 5,服务器 2 被赋予的权值为 1,那么 (1, 2, 3, 4, 5) 请求会被发送到服务器 1,(6) 请求会被发送到服务器 2。
|
||||
|
||||
<div align="center"> <img src="../pics//211c60d4-75ca-4acd-8a4f-171458ed58b4.jpg"/> </div><br>
|
||||
|
||||
### 3. 最少连接(least Connections)
|
||||
|
||||
由于每个请求的连接时间不一样,使用轮询或者加权轮询算法的话,可能会让一台服务器当前连接数过大,而另一台服务器的连接过小,造成负载不均衡。例如下图中,(1, 3, 5) 请求会被发送到服务器 1,但是 (1, 3) 很快就断开连接,此时只有 (5) 请求连接服务器 1;(2, 4, 6) 请求被发送到服务器 2,只有 (2) 的连接断开。该系统继续运行时,服务器 2 会承担过大的负载。
|
||||
|
||||
<div align="center"> <img src="../pics//3b0d1aa8-d0e0-46c2-8fd1-736bf08a11aa.jpg"/> </div><br>
|
||||
|
||||
最少连接算法就是将请求发送给当前最少连接数的服务器上。例如下图中,服务器 1 当前连接数最小,那么新到来的请求 6 就会被发送到服务器 1 上。
|
||||
|
||||
<div align="center"> <img src="../pics//1f4a7f10-52b2-4bd7-a67d-a9581d66dc62.jpg"/> </div><br>
|
||||
|
||||
### 4. 加权最少连接(Weighted Least Connection)
|
||||
|
||||
在最少连接的基础上,根据服务器的性能为每台服务器分配权重,再根据权重计算出每台服务器能处理的连接数。
|
||||
|
||||
<div align="center"> <img src="../pics//44edefb7-4b58-4519-b8ee-4aca01697b78.jpg"/> </div><br>
|
||||
|
||||
### 5. 随机算法(Random)
|
||||
|
||||
把请求随机发送到服务器上。和轮询算法类似,该算法比较适合服务器性能差不多的场景。
|
||||
|
||||
<div align="center"> <img src="../pics//0ee0f61b-c782-441e-bf34-665650198ae0.jpg"/> </div><br>
|
||||
|
||||
### 6. 源地址哈希法 (IP Hash)
|
||||
|
||||
源地址哈希通过对客户端 IP 哈希计算得到的一个数值,用该数值对服务器数量进行取模运算,取模结果便是目标服务器的序号。
|
||||
|
||||
- 优点:保证同一 IP 的客户端都会被 hash 到同一台服务器上。
|
||||
- 缺点:不利于集群扩展,后台服务器数量变更都会影响 hash 结果。可以采用一致性 Hash 改进。
|
||||
|
||||
<div align="center"> <img src="../pics//2018040302.jpg"/> </div><br>
|
||||
|
||||
## 实现
|
||||
|
||||
### 1. HTTP 重定向
|
||||
|
||||
HTTP 重定向负载均衡服务器收到 HTTP 请求之后会返回服务器的地址,并将该地址写入 HTTP 重定向响应中返回给浏览器,浏览器收到后需要再次发送请求。
|
||||
|
||||
缺点:
|
||||
|
||||
- 用户访问的延迟会增加;
|
||||
- 如果负载均衡器宕机,就无法访问该站点。
|
||||
|
||||
<div align="center"> <img src="../pics//10bdf7bf-0daa-4a26-b927-f142b3f8e72b.png"/> </div><br>
|
||||
|
||||
### 2. DNS 重定向
|
||||
|
||||
使用 DNS 作为负载均衡器,根据负载情况返回不同服务器的 IP 地址。大型网站基本使用了这种方式做为第一级负载均衡手段,然后在内部使用其它方式做第二级负载均衡。
|
||||
|
||||
缺点:
|
||||
|
||||
- DNS 查找表可能会被客户端缓存起来,那么之后的所有请求都会被重定向到同一个服务器。
|
||||
|
||||
<div align="center"> <img src="../pics//f8b16d1e-7363-4544-94d6-4939fdf849dc.png"/> </div><br>
|
||||
|
||||
### 3. 修改 MAC 地址
|
||||
|
||||
使用 LVS(Linux Virtual Server)这种链路层负载均衡器,根据负载情况修改请求的 MAC 地址。
|
||||
|
||||
<div align="center"> <img src="../pics//f0e35b7a-2948-488a-a5a9-97d3f6b5e2d7.png"/> </div><br>
|
||||
|
||||
### 4. 修改 IP 地址
|
||||
|
||||
在网络层修改请求的目的 IP 地址。
|
||||
|
||||
<div align="center"> <img src="../pics//265a355d-aead-48aa-b455-f33b62fe729f.png"/> </div><br>
|
||||
|
||||
### 5. 代理自动配置
|
||||
|
||||
正向代理与反向代理的区别:
|
||||
|
||||
- 正向代理:发生在客户端,是由用户主动发起的。比如翻墙,客户端通过主动访问代理服务器,让代理服务器获得需要的外网数据,然后转发回客户端。
|
||||
- 反向代理:发生在服务器端,用户不知道代理的存在。
|
||||
|
||||
PAC 服务器是用来判断一个请求是否要经过代理。
|
||||
|
||||
<div align="center"> <img src="../pics//52e1af6f-3a7a-4bee-aa8f-fcb5dacebe40.jpg"/> </div><br>
|
||||
|
||||
|
||||
|
||||
# 参考资料
|
||||
|
||||
- [Comparing Load Balancing Algorithms](http://www.jscape.com/blog/load-balancing-algorithms)
|
||||
- [负载均衡算法及手段](https://segmentfault.com/a/1190000004492447)
|
||||
- [Redirection and Load Balancing](http://slideplayer.com/slide/6599069/#)
|
||||
- [Session Management using Spring Session with JDBC DataStore](https://sivalabs.in/2018/02/session-management-using-spring-session-jdbc-datastore/)
|
||||
- [Apache Wicket User Guide - Reference Documentation](https://ci.apache.org/projects/wicket/guide/6.x/)
|
||||
- [集群/分布式环境下 5 种 Session 处理策略](http://blog.csdn.net/u010028869/article/details/50773174?ref=myread)
|
||||
- [浅谈分布式锁](http://www.linkedkeeper.com/detail/blog.action?bid=1023)
|
||||
- [深入理解分布式事务](https://juejin.im/entry/577c6f220a2b5800573492be)
|
||||
- [分布式系统的事务处理](https://coolshell.cn/articles/10910.html)
|
||||
- [关于分布式事务](http://blog.csdn.net/suifeng3051/article/details/52691210)
|
||||
- [基于 Zookeeper 的分布式锁](http://www.dengshenyu.com/java/%E5%88%86%E5%B8%83%E5%BC%8F%E7%B3%BB%E7%BB%9F/2017/10/23/zookeeper-distributed-lock.html)
|
||||
- [微服务场景下的数据一致性解决方案](https://opentalk.upyun.com/310.html)
|
||||
- [聊聊分布式事务,再说说解决方案](https://www.cnblogs.com/savorboard/p/distributed-system-transaction-consistency.html)
|
BIN
pics/04f41228-375d-4b7d-bfef-738c5a7c8f07.jpg
Normal file
After Width: | Height: | Size: 22 KiB |
BIN
pics/0b587744-c0a8-46f2-8d72-e8f070d67b4b.jpg
Normal file
After Width: | Height: | Size: 43 KiB |
BIN
pics/1a9977e4-2f5c-49a6-aec9-f3027c9f46a7.png
Normal file
After Width: | Height: | Size: 46 KiB |
BIN
pics/2991c772-fb1c-4051-a9c7-932b68e76bd7.jpg
Normal file
After Width: | Height: | Size: 21 KiB |
BIN
pics/2bcc58ad-bf7f-485c-89b5-e7cafc211ce2.jpg
Normal file
After Width: | Height: | Size: 58 KiB |
BIN
pics/9b838aee-0996-44a5-9b0f-3d1e3e2f5100.png
Normal file
After Width: | Height: | Size: 38 KiB |
BIN
pics/b988877c-0f0a-4593-916d-de2081320628.jpg
Normal file
After Width: | Height: | Size: 30 KiB |
BIN
pics/bc603930-d74d-4499-a3e7-2d740fc07f33.png
Normal file
After Width: | Height: | Size: 14 KiB |
BIN
pics/bf667594-bb4b-4634-bf9b-0596a45415ba.jpg
Normal file
After Width: | Height: | Size: 27 KiB |
BIN
pics/f1109d04-3c67-48a3-9963-2c475f94e175.jpg
Normal file
After Width: | Height: | Size: 32 KiB |
BIN
pics/fb44307f-8e98-4ff7-a918-31dacfa564b4.jpg
Normal file
After Width: | Height: | Size: 50 KiB |