【Java劝退师】分布式理论 知识脑图 | 🏆 技术专题第五期征文

2,774 阅读9分钟

Distribute

分布式理论

分布式系统 : 硬件或软件分布在不同的网络计算机上,彼此间透过消息进行通信或协调的系统

一、解决的问题 (单体架构缺点)

  1. 对海量用户处理能力有限
  2. 程序复杂度越高,开发效率越低
  3. 生产环境发生重大BUG,将导致服务全数停摆
  4. 代码量增加,编译效率下降
  5. 只能关注一套技术栈

二、名词解释

  1. 分布式 - 多个人在一起做【不同】的事

  2. 集群 - 多个人在一起做【相同】的事

  3. 网络分区(脑裂) - 网络之间不连通,导致分布式系统出现局部小集群,小集群间网络异常,小集群内网络正常

三、架构演进

  1. 单应用架构
  2. 应用服务器 与 数据服务器 分离
  3. 应用服务器 集群
  4. 数据库读写分离
  5. 增添搜索引擎缓解读库压力
  6. 增添缓存缓解读库压力
  7. 数据库 水平/垂直 拆分
  8. 应用服务器 垂直拆分
  9. 应用服务器 水平拆分

四、一致性分类

  1. 强一致性 - 要求系统写入什么,读出来也是什么 - 性能影响大
  2. 弱一致性 - 不承诺多久后数据能达成一致,尽可能在某个级别(秒、分、时)达到一致状态
  3. 读写一致性 - 第一时间看到自己更新的内容,其他人不保证
    • 特定内容从主库读取 - 主库压力大
    • 刚更新的内容从主库读取,过段时间后,从从库读取
  4. 最终一致性 - 仅保证最终系统内,所有副本的数据是正确的

五、CAP 定理

  1. Consistency 一致性 - 所有副本数据一致,从任意节点读取到的数据都是最新的
  2. Availability 可用性 - 对外提供的服务保持正常,不会出现响应超时或响应错误
  3. Partition tolerance 分区容错性 - 当出现网络分区时,仍可对外提供服务

三个要求只能同时满足其中两个

证明 : 用户向 N1 发出请求,将值从 V0 改成 V1,此时 N1、N2 之间的网络中断,然而又有一个用户向 N2 发出请求取得 Value,此时有三种做法

(1) 将 V0 返回【AP 模式,牺牲一致性】

(2) 等待网络恢复,再返回 V1 【CP模式,牺牲可用性】

(3) 将 N1、N2 合并【CA模式,舍弃分布式技术】

六、BASE 理论

对 CAP 定理的权衡结果,如果无法做到强一致性,则应根据业务特点,以适当方式达成最终一致性

  1. Basically Available 基本可用 - 当分布式系统出现故障时,允许损失部分可用性
    • 时间 : 正常于 0.5 秒内响应结果,故障时增加到 1~2 秒
    • 功能 : 流量激增时,将部分用户引导到降级页面
  2. Soft state 软状态 - 允许数据存在中间状态(有部分数据尚未完成同步),但不影响系统整体可用性
  3. Eventually consistent 最终一致性 - 经过一段时间的同步后,数据最终能达到一致

七、一致性协议 (处理数据库事务)

1. 2PC 两阶段提交

流程

  1. 准备阶段 - 协调者 给每个 参与者 发送 Prepare 消息,运行本地事务但不提交
  2. 提交阶段 - 协调者 收到 参与者 运行失败或超时消息,则向 参与者 发送 Rollback 消息,否则发送 Commit 消息

缺点

  1. 同步阻塞 - 在一阶段未达二阶段时,参与者事务都处于阻塞状态
  2. 单点问题 - 如果协调者在运行二阶段前崩溃,参与者事务将处于锁定状态
  3. 数据不一致 - 协调者在尚未发送完 Commit 消息就崩溃,将导致数据不一致
  4. 过于保守 - 任一节点失败,将导致整个事务失败

2. 3PC 三阶段提交

流程

  1. CanCommit - 协调者 给每个 参与者 发送 包含事务的请求,询问是否可以运行
  2. PreCommit - 协调者 要求 参与者 运行事务
  3. DoCommit - 协调者 要求 参与者 提交事务 - 此阶段 参与者 若无法收到 协调者 消息,超时默认提交事务

降低了 2PC 的事务阻塞范围,但并未完全解决数据不一致问题

八、一致性算法 (选出 最终结果 或 Leader)

1. Paxos 算法

角色

  1. Client 客户端 - 向分布式系统发出请求
  2. Proposer 提案发起者 - 说服 Acceptor 达成一致
  3. Acceptor 决策者 - 批准提案
  4. Learner 学习者 - 学习最终决策

规范

  1. 一个 Acceptor 必须接受它收到的第一个提案
  2. 每次收到的提案值,都必须跟第一次一样
  3. 一个提案被选定,需要被半数以上的 Acceptor 接受

**流程 1 **

  1. Proposer 向半数以上的 Acceptor 发起编号为 N 但没有 Value 的 prepare 请求
  2. 如果 Acceptor 未接受过提案,则返回 Null
  3. 此时 Proposer 可以自行决定 Value 值,向 Acceptor 发起 编号为 N,值为 Value 的 accept 请求
  4. Acceptor 接受编号为 N,值为 Value 的提案

流程 2

  1. Proposer 向半数以上的 Acceptor 发起编号为 N+1 但没有 Value 的 prepare 请求
  2. 如果 Acceptor 已经接受过编号为 N 的提案,则返回提案 N 的 Value 值
  3. 此时 Proposer 向 Acceptor 发起 编号为 N+1 值为 Value 的 accept 请求
  4. Acceptor 接受编号为 N+1,值为 Value 的提案

极端情况 : 两个 Proposer 依次提交编号递增的提案,导致死循环

解决 : 规定只有主 Proposer 才能提交提案

2. Raft 算法

角色

  1. Leader 领导者 - 与客户端交互,只有一个
  2. Candidate 候选者 - 负责在选举过程中提名自己,当选举成功,成为领导者
  3. Follower 跟随者 - 选民,等待投票通知

流程

  1. 选举开始,所有节点都是 Follower
  2. 如果收到 RequestVote (投票给我) 、AppendEntries (已选出Leader) 的请求,则保持 Follower 状态
  3. 一段时间( 随机 150 ~ 300ms )内没收到请求,则将身分转换为 Candidate 开始竞选 Leader,如果获得过半票数则成为 Leader
  4. 如果最后未选出 Leader,则 Term + 1,开启下一轮选举

九、幂等性

运行多次操作产生的影响,与只运行一次操作产生的影响相同

1. 需要幂等性情况

  • 用户重复提交表单

  • 用户恶意重复投票

  • 接口超时重试请求

  • 消息进行重复消费

    须避免因重试造成系统产生未知的问题

2. 解决方案

  • 数据库字段唯一

    • 适用操作
      • 添加
      • 删除
    • 限制
      • 数据表中需包含唯一字段
    • 操作流程
      1. 上游服务 产生 ID (字段唯一),在调用下游服务时,将 ID 传入
      2. 下游服务 在 添加数据 时,如数据已存在则报错
  • 数据库乐观锁

    • 适用操作
      • 更新
    • 限制
      • 需要在数据表中添加额外的字段
    • 操作流程
      1. 上游服务 将 要修改记录 的 当前 version 值,传给下游服务

      2. 下游服务 在 修改记录前,先检查 version 是否匹配,匹配成功再更新,并把 version 值 + 1

  • Token 令牌

    • 适用操作
      • 添加
      • 更新
      • 删除
    • 限制
      • 需使用 Redis 进行校验
    • 适用场景
      • 用户提交订单
    • 操作流程
      1. 当 数据库纪录 添加成功时,将用户的 Token 值作为 Redis 的 Key,在 Redis 中插入一条具备存活时长的记录(10秒)
      2. 当向数据库 添加数据 时,查找 Redis,如果记录存在,即表示重复添加
  • 唯一串行号

    • 适用操作
      • 添加
      • 更新
      • 删除
    • 限制
      • 需使用 Redis 进行校验
    • 适用场景
      • 服务间接口调用
    • 操作流程
      1. 上游服务 生成 唯一串行号,作为 Redis 的 Key,存入到 Redis 中,并且将串行号传给 下游服务
      2. 下游服务 在 添加数据 前,检查 Redis 中是否存在记录,如存在表示第一次添加,添加后删除 Redis 中的记录

十、分布式系统设计策略

1. 心跳检测

通常携带状态、元数据信息,方便管理

  • 周期心跳检测 - 响应超时,判定死亡
  • 累计失效检测 - 对濒临死亡的节点,发起有限次数重试

2. 高可用

  • 主备模式【常用】 - 主机当机,备机接管主机的一切工作 - 主机恢复后,通过自动(热备)或手动(冷备)方式将服务切换回主机 - MySQL、Redis
  • 互备模式(多主) - 两台主机同时运行,并且相互监测
  • 集群模式 - 多个节点同时运行,透过主节点分担请求 - 需解决主节点高可用问题

3. 负载均衡

解决方案

  • 硬件 - F5
  • 软件 - LVS、HAProxy、Nginx

策略

  • 随机
  • 轮询
  • 权重
  • 最少连接
  • IP 哈希

十一、网络通信

1. RPC

Remote Procedure Call 远程过程调用

角色

  1. Client 客户端 - 服务调用方
  2. Client Stub 客户端存根 - 将客户端请求打包成网络消息,通过网络发送给 Server Stub
  3. Server Stub 服务端存根 - 接收 Client Stub 消息,将消息解包,调用本地方法
  4. Server 服务端 - 服务提供者
image-20201029111955331

2. RMI

Remote Method Invocation 远程方法调用

Java 原生支持,用于不同 Java 虚拟机之间的通信

角色

客户端

  1. Stub 存根 - 客户端代理
  2. Remote Reference Layer 远程引用层 - 解析并运行远程引用协议
  3. Transport 传输层 - 调用远程方法,接收运行结果

服务端

  1. Transport 传输层 - 接收客户端请求,转发请求到 Remote Reference Layer
  2. Remote Reference Layer 远程引用层 - 调用 Skeleton 方法
  3. Skeleton 骨架 - 调用实际方法

注册表 Registry - 以 URL 注册远程对象,并向客户端回复远程对象的引用

image-20201029112552158

十二、IO

1. 名词解释

zhuanlan.zhihu.com/p/22707398

  1. 阻塞 - 发起调用后,立即被挂起,直到系统读取完数据返回给应用层

  2. 非阻塞 - 发起调用后,立即返回

  3. 同步 - 主动向系统内核询问,数据是否读取完成

  4. 异步 - 不向系统内核询问,而是系统内核读取完成后,主动通知

image-20201029114217877
image-20201029114237853

2. 类型

  1. BIO 同步阻塞IO - 一个连接一个线程

    • 简单
    • 资源开销高
    image-20201029120919563
  2. NIO 同步非阻塞IO - 一个连接一个信道 - 将连接注册到多路复用器上,多路复用器轮询信道,当信道有数据时才启动线程处理

    image-20201029120735371
  3. AIO 异步非阻塞IO - 连接操作都仅委托OS,OS负责通知服务 及 服务完成回调

image-20201029120645299

🏆 技术专题第五期 | 聊聊分布式的那些事......