分布式理论
分布式系统 : 硬件或软件分布在不同的网络计算机上,彼此间透过消息进行通信或协调的系统
一、解决的问题 (单体架构缺点)
- 对海量用户处理能力有限
- 程序复杂度越高,开发效率越低
- 生产环境发生重大BUG,将导致服务全数停摆
- 代码量增加,编译效率下降
- 只能关注一套技术栈
二、名词解释
-
分布式 - 多个人在一起做【不同】的事
-
集群 - 多个人在一起做【相同】的事
-
网络分区(脑裂) - 网络之间不连通,导致分布式系统出现局部小集群,小集群间网络异常,小集群内网络正常
三、架构演进
- 单应用架构
- 应用服务器 与 数据服务器 分离
- 应用服务器 集群
- 数据库读写分离
- 增添搜索引擎缓解读库压力
- 增添缓存缓解读库压力
- 数据库 水平/垂直 拆分
- 应用服务器 垂直拆分
- 应用服务器 水平拆分
四、一致性分类
- 强一致性 - 要求系统写入什么,读出来也是什么 - 性能影响大
- 弱一致性 - 不承诺多久后数据能达成一致,尽可能在某个级别(秒、分、时)达到一致状态
- 读写一致性 - 第一时间看到自己更新的内容,其他人不保证
- 特定内容从主库读取 - 主库压力大
- 刚更新的内容从主库读取,过段时间后,从从库读取
- 最终一致性 - 仅保证最终系统内,所有副本的数据是正确的
五、CAP 定理
- Consistency 一致性 - 所有副本数据一致,从任意节点读取到的数据都是最新的
- Availability 可用性 - 对外提供的服务保持正常,不会出现响应超时或响应错误
- Partition tolerance 分区容错性 - 当出现网络分区时,仍可对外提供服务
三个要求只能同时满足其中两个
证明 : 用户向 N1 发出请求,将值从 V0 改成 V1,此时 N1、N2 之间的网络中断,然而又有一个用户向 N2 发出请求取得 Value,此时有三种做法
(1) 将 V0 返回【AP 模式,牺牲一致性】
(2) 等待网络恢复,再返回 V1 【CP模式,牺牲可用性】
(3) 将 N1、N2 合并【CA模式,舍弃分布式技术】
六、BASE 理论
对 CAP 定理的权衡结果,如果无法做到强一致性,则应根据业务特点,以适当方式达成最终一致性
- Basically Available 基本可用 - 当分布式系统出现故障时,允许损失部分可用性
- 时间 : 正常于 0.5 秒内响应结果,故障时增加到 1~2 秒
- 功能 : 流量激增时,将部分用户引导到降级页面
- Soft state 软状态 - 允许数据存在中间状态(有部分数据尚未完成同步),但不影响系统整体可用性
- Eventually consistent 最终一致性 - 经过一段时间的同步后,数据最终能达到一致
七、一致性协议 (处理数据库事务)
1. 2PC 两阶段提交
流程
- 准备阶段 - 协调者 给每个 参与者 发送 Prepare 消息,运行本地事务但不提交
- 提交阶段 - 协调者 收到 参与者 运行失败或超时消息,则向 参与者 发送 Rollback 消息,否则发送 Commit 消息
缺点
- 同步阻塞 - 在一阶段未达二阶段时,参与者事务都处于阻塞状态
- 单点问题 - 如果协调者在运行二阶段前崩溃,参与者事务将处于锁定状态
- 数据不一致 - 协调者在尚未发送完 Commit 消息就崩溃,将导致数据不一致
- 过于保守 - 任一节点失败,将导致整个事务失败
2. 3PC 三阶段提交
流程
- CanCommit - 协调者 给每个 参与者 发送 包含事务的请求,询问是否可以运行
- PreCommit - 协调者 要求 参与者 运行事务
- DoCommit - 协调者 要求 参与者 提交事务 - 此阶段 参与者 若无法收到 协调者 消息,超时默认提交事务
降低了 2PC 的事务阻塞范围,但并未完全解决数据不一致问题
八、一致性算法 (选出 最终结果 或 Leader)
1. Paxos 算法
角色
- Client 客户端 - 向分布式系统发出请求
- Proposer 提案发起者 - 说服 Acceptor 达成一致
- Acceptor 决策者 - 批准提案
- Learner 学习者 - 学习最终决策
规范
- 一个 Acceptor 必须接受它收到的第一个提案
- 每次收到的提案值,都必须跟第一次一样
- 一个提案被选定,需要被半数以上的 Acceptor 接受
**流程 1 **
- Proposer 向半数以上的 Acceptor 发起编号为 N 但没有 Value 的 prepare 请求
- 如果 Acceptor 未接受过提案,则返回 Null
- 此时 Proposer 可以自行决定 Value 值,向 Acceptor 发起 编号为 N,值为 Value 的 accept 请求
- Acceptor 接受编号为 N,值为 Value 的提案
流程 2
- Proposer 向半数以上的 Acceptor 发起编号为 N+1 但没有 Value 的 prepare 请求
- 如果 Acceptor 已经接受过编号为 N 的提案,则返回提案 N 的 Value 值
- 此时 Proposer 向 Acceptor 发起 编号为 N+1 值为 Value 的 accept 请求
- Acceptor 接受编号为 N+1,值为 Value 的提案
极端情况 : 两个 Proposer 依次提交编号递增的提案,导致死循环
解决 : 规定只有主 Proposer 才能提交提案
2. Raft 算法
角色
- Leader 领导者 - 与客户端交互,只有一个
- Candidate 候选者 - 负责在选举过程中提名自己,当选举成功,成为领导者
- Follower 跟随者 - 选民,等待投票通知
流程
- 选举开始,所有节点都是 Follower
- 如果收到 RequestVote (投票给我) 、AppendEntries (已选出Leader) 的请求,则保持 Follower 状态
- 一段时间( 随机 150 ~ 300ms )内没收到请求,则将身分转换为 Candidate 开始竞选 Leader,如果获得过半票数则成为 Leader
- 如果最后未选出 Leader,则 Term + 1,开启下一轮选举
九、幂等性
运行多次操作产生的影响,与只运行一次操作产生的影响相同
1. 需要幂等性情况
-
用户重复提交表单
-
用户恶意重复投票
-
接口超时重试请求
-
消息进行重复消费
须避免因重试造成系统产生未知的问题
2. 解决方案
-
数据库字段唯一
- 适用操作
- 添加
- 删除
- 限制
- 数据表中需包含唯一字段
- 操作流程
- 上游服务 产生 ID (字段唯一),在调用下游服务时,将 ID 传入
- 下游服务 在 添加数据 时,如数据已存在则报错
- 适用操作
-
数据库乐观锁
- 适用操作
- 更新
- 限制
- 需要在数据表中添加额外的字段
- 操作流程
-
上游服务 将 要修改记录 的 当前 version 值,传给下游服务
-
下游服务 在 修改记录前,先检查 version 是否匹配,匹配成功再更新,并把 version 值 + 1
-
- 适用操作
-
Token 令牌
- 适用操作
- 添加
- 更新
- 删除
- 限制
- 需使用 Redis 进行校验
- 适用场景
- 用户提交订单
- 操作流程
- 当 数据库纪录 添加成功时,将用户的 Token 值作为 Redis 的 Key,在 Redis 中插入一条具备存活时长的记录(10秒)
- 当向数据库 添加数据 时,查找 Redis,如果记录存在,即表示重复添加
- 适用操作
-
唯一串行号
- 适用操作
- 添加
- 更新
- 删除
- 限制
- 需使用 Redis 进行校验
- 适用场景
- 服务间接口调用
- 操作流程
- 上游服务 生成 唯一串行号,作为 Redis 的 Key,存入到 Redis 中,并且将串行号传给 下游服务
- 下游服务 在 添加数据 前,检查 Redis 中是否存在记录,如存在表示第一次添加,添加后删除 Redis 中的记录
- 适用操作
十、分布式系统设计策略
1. 心跳检测
通常携带状态、元数据信息,方便管理
- 周期心跳检测 - 响应超时,判定死亡
- 累计失效检测 - 对濒临死亡的节点,发起有限次数重试
2. 高可用
- 主备模式【常用】 - 主机当机,备机接管主机的一切工作 - 主机恢复后,通过自动(热备)或手动(冷备)方式将服务切换回主机 - MySQL、Redis
- 互备模式(多主) - 两台主机同时运行,并且相互监测
- 集群模式 - 多个节点同时运行,透过主节点分担请求 - 需解决主节点高可用问题
3. 负载均衡
解决方案
- 硬件 - F5
- 软件 - LVS、HAProxy、Nginx
策略
- 随机
- 轮询
- 权重
- 最少连接
- IP 哈希
十一、网络通信
1. RPC
Remote Procedure Call 远程过程调用
角色
- Client 客户端 - 服务调用方
- Client Stub 客户端存根 - 将客户端请求打包成网络消息,通过网络发送给 Server Stub
- Server Stub 服务端存根 - 接收 Client Stub 消息,将消息解包,调用本地方法
- Server 服务端 - 服务提供者
2. RMI
Remote Method Invocation 远程方法调用
Java 原生支持,用于不同 Java 虚拟机之间的通信
角色
客户端
- Stub 存根 - 客户端代理
- Remote Reference Layer 远程引用层 - 解析并运行远程引用协议
- Transport 传输层 - 调用远程方法,接收运行结果
服务端
- Transport 传输层 - 接收客户端请求,转发请求到 Remote Reference Layer
- Remote Reference Layer 远程引用层 - 调用 Skeleton 方法
- Skeleton 骨架 - 调用实际方法
注册表 Registry - 以 URL 注册远程对象,并向客户端回复远程对象的引用
十二、IO
1. 名词解释
-
阻塞 - 发起调用后,立即被挂起,直到系统读取完数据返回给应用层
-
非阻塞 - 发起调用后,立即返回
-
同步 - 主动向系统内核询问,数据是否读取完成
-
异步 - 不向系统内核询问,而是系统内核读取完成后,主动通知
2. 类型
-
BIO 同步阻塞IO - 一个连接一个线程
- 简单
- 资源开销高
-
NIO 同步非阻塞IO - 一个连接一个信道 - 将连接注册到多路复用器上,多路复用器轮询信道,当信道有数据时才启动线程处理
-
AIO 异步非阻塞IO - 连接操作都仅委托OS,OS负责通知服务 及 服务完成回调