分布式
分布式系统:一组独立的计算单元通过网络进行通信并透明地对外提供服务
为什么要分布式
- 模块耦合过高很可能造成牵一发动全身
- 代码发布成本很高
- 提高开发效率
分布式系统解决了什么问题
- 单机性能瓶颈导致的成本问题 小型机器不够用 大型机器又很贵
- 用户量以及数据量增大不得不使用分布式
- 业务高可用的要求
技术栈
- 提高性能:缓存、负载均衡、异步、数据复制与分区
- 提高稳定性:拆分、冗余、降级、高可用、运维
关键技术:服务治理、架构管理、DevOps、自动化运维、资源调度管理、监控、流量控制
分布式系统的麻烦
系统的某些部分可能会以某种不可预知的方式被破坏。这被称为部分失效(partial failure)。这种失效重点在于由于分布式系统通过网络来进行协作,所以是不确定性的
分布式系统的故障处理原则:
- 真理由多数所定义:当一个数据的值被多数节点所认可时,这个值才是正确的,多数节点被称为法定人数,最常见的法定人数是超过一半的绝对多数
- 非拜占庭故障:所谓分布式系统的网络大都由某个组织所控制
网络不可靠
分布式系统大都通过节点之间发送消息来协作。但网络是不可靠的,有时消息发没发到,接没接到,这些都是不确定的。处理这个问题的通常方法是超时(Timeout):在一段时间之后放弃等待,并且认为响应不会到达,人为原因是造成网络中断的主要原因
为了检测分布式系统节点的故障,一些机制可以知道节点是否关闭了,比如TCP协议中发送FIN或者RST,通过探测整个网络拓扑结构来发现网络是否故障。但这些仍无法百分百确保能接收到节点关闭的消息,所以可以通过重试,无论是TCP层的重试,还是应用层的重试,来进行等待,超时则声明节点死亡。
既然超时是唯一的解决方案,那超时应该超多久?这个时间应该是动态调整的,由于网络是通过动态分配流量来提升资源利用率,所以只能通过实验方式选择超时:测量延长的网络往返时间和多台机器的分布,以确定超时时间
不同于电话网这种事先为每个用户预留一定资源的网络,以太网这种网络更像一种抢占性的网络,所以电话网这种网络更有可能达到可预测的延时时间目标。
节点失效模型:
- 崩溃-中止故障:算法可能会假设一个节点只能以崩溃方式失效
- 崩溃-恢复故障:可能会在任何时候崩溃,但也许会在未知的时间之后再次开始响应
- 拜占庭故障:节点可以做(绝对意义上的)任何事情,包括试图戏弄和欺骗其他节点
时钟不可靠
计算机中的石英钟不够精确:它会漂移(drifts)(运行速度快于或慢于预期)
- 单调钟:单调钟的绝对值是毫无意义的:它可能是计算机启动以来的纳秒数,或类似的任意值
- 时钟:有意义的时间,通常与NTP同步
由于时钟的不准确性,时钟应该是要有一个可信区间:[不早于, 不晚于] 如果没有重叠就能使用这个区间判断两个时钟的先后顺序
为了解决时钟漂移带来的不同节点时间不一致的问题,可以使用:
逻辑时钟(logic clock):类似于事务版本号
另外一个需要注意额是进程暂停问题,无论是JVM的STW或者操作系统的进程重新调度,可能都会造成进程被冻结一段时间,然后会来重新执行,此时如果没有对时间做特殊处理,可能就会出现问题。分布式系统中的节点,必须假定其执行可能在任意时刻暂停相当长的时间
为了解决这个问题,可以引入版本号,当资源持有者发现请求者的版本号不是最新的时候就可以拒绝请求
为了达到响应时间的保证,一些实时系统可能会通过降低吞吐量的方式来提升实时性。
定时假设,三种系统模型是常用的:
- 同步模型(synchronous model)假设网络延迟,进程暂停和和时钟误差都是有界限的
- 部分同步(partial synchronous)意味着一个系统在大多数情况下像一个同步系统一样运行,但有时候会超出网络延迟,进程暂停和时钟漂移的界限
- 异步模型 一个算法不允许对时机做任何假设——事实上它甚至没有时钟
一致性与共识
线性化
在一个线性化的系统中,只要一个客户端成功完成写操作,所有客户端从数据库中读取数据必须能够看到刚刚写入的值
使用线性化的场景:
- 加锁、主节点选举
- 唯一性保证
线性化背后的基本思想很简单:使系统看起来好像只有一个数据副本。
可实现线性化的复制方式:
- 主从复制:写后在主节点上读
- 共识算法
顺序保证
- 因果关系与因果一致性
为了知道因果关系,需要知道请求的先后顺序,为了知道请求先后顺序,需要知道读取或者写入数据的版本
为了产生这个版本,使用时间戳是不可靠的,在单个节点内可以使用逻辑时间戳
全序关系广播
- 可靠发送接收消息
- 消息之间严格有序