读构建可扩展分布式系统:方法与实践03分布式系统要点
1. 通信基础
1.1. 每个分布式系统都包含通过网络进行通信的软件组件
1.2. 硬件
-
1.2.1. 全球互联网就是一台异构机器,由不同类型的网络通信通道和设备组成,它们每秒将数百万条消息通过网络传送到预定目的地
-
1.2.2. 对于单个光纤链路,可以提供每秒超过70 Tbps的总带宽
-
1.2.3. 广域网的延迟更为复杂
- 1.2.3.1. 广域网传输数据的距离可达数百乃至数千公里,数据在光纤电缆中传输的最大速度是理论光速
-
1.2.4. 路由器是专用高速网络设备,它可以处理数百Gbps的网络流量,从传入连接中提取数据并根据目的地将数据发送到不同的传出网络连接
-
1.2.4.1. 家里和办公室中的WiFi路由器都是无线以太网网络,使用802.11协议发送和接收数据
-
1.2.4.2. 使用最广泛的WiFi协议是802.11ac(WiFi 5),它允许最大(理论)数据速率高达5400 Mbps
-
1.2.4.3. 最新的802.11ax协议,也称WiFi 6,是由802.11ac技术演变的,声称已将吞吐量提高至9.6 Gbps
-
1.2.4.4. WiFi路由器的范围是几十米的数量级,会受到墙壁和地板等物理障碍的影响
-
-
1.2.5. 蜂窝无线技术使用无线电波将数据从手机发送到安装在手机信号塔上的路由器,路由器通常通过电线连接到核心互联网来进行消息路由
-
1.2.5.1. 5G基站的最大覆盖范围约为500 m
-
1.2.5.2. 4G可在10~15 km内提供可靠的接收
-
-
1.2.6. 一级网络是全球高速互联网骨干网
- 1.2.6.1. 大约有20家一级ISP(互联网服务提供商)负责管理和控制全球流量
-
1.2.7. 二级ISP通常是区域性
- 1.2.7.1. 带宽低于一级ISP,并通过三级ISP向客户提供内容
-
1.2.8. 三级ISP是每月向你收取高额家庭互联网费用的ISP
1.3. 软件
-
1.3.1. 互联网上的软件系统使用IP(互联网协议)套件进行通信
-
1.3.1.1. 互联网是一个数据包交换网络,每个数据包都在网络中单独路由
-
1.3.1.2. 因为网络数据包在发送方和接收方之间的传输过程中可能会丢失或延迟,所以可靠性是必需的
-
-
1.3.2. 四个抽象层
-
1.3.2.1. 数据链路层,定义了跨单个网段的数据通信方法
-
1.3.2.2. 网络层,指定寻址和路由协议,使流量能够穿越构成互联网的独立管理和控制的网络
1.3.2.2.1. 是互联网协议家族中的IP层
-
1.3.2.3. 传输层,定义了可靠和尽力而为的主机到主机通信的协议
1.3.2.3.1. 是著名的TCP(传输控制协议)和UDP(用户数据报协议)所在的层
-
1.3.2.4. 应用层,其中包含多个应用程序级协议
1.3.2.4.1. HTTP和SCP(安全复制协议)
-
-
1.3.3. IP
-
1.3.3.1. IP定义了如何在互联网上为主机分配地址,以及如何在知道彼此地址的两个主机之间传输消息
-
1.3.3.2. 互联网上的每个设备都有自己的地址,也称为IP地址
-
1.3.3.3. IP地址的位置可以在互联网范围的目录服务DNS(域名系统)中找到
1.3.3.3.1. DNS服务器是分层组织的
1.3.3.3.2. 整个DNS数据库在地理上高度复制,因此没有单点故障,同时请求被分布在多个物理服务器上
-
1.3.3.4. 用于分配IP地址的技术称为IPv4(互联网通信协议第4版),它将被后继者IPv6取代
1.3.3.4.1. IPv4是32位寻址方案,由于连接到互联网的设备数量不断增加,不久的将来就会用完地址
-
1.3.3.5. IPv6是128位方案,提供(几乎)无限数量的IP地址
-
1.3.3.6. IP也被称为尽力而为的传输协议,它不会补偿数据包传输期间可能发生的各种错误情况
1.3.3.6.1. 可能的传输错误包括数据损坏、数据包丢失和重复
-
-
1.3.4. TCP
-
1.3.4.1. 一旦应用程序或浏览器发现了目标服务器的IP地址,它就可以使用传输协议API发送消息
1.3.4.1.1. 这是使用TCP或UDP来实现的,它们是IP网络协议栈的流行标准传输协议
-
1.3.4.2. 面向连接的
-
1.3.4.3. 面向流的
-
1.3.4.4. 可靠的
-
1.3.4.5. TCP是面向连接的协议
1.3.4.5.1. 在应用程序之间交换任何消息之前,TCP使用三步握手在客户端和服务器应用程序之间建立双向连接
-
1.3.4.6. 用于检查数据包完整性的校验和
-
1.3.4.7. 用于确保发送方不会因发送数据过快而淹没慢速接收方的动态流量控制
-
1.3.4.8. 连同连接建立和确认,TCP成为一个重量级的协议,它在可靠性和效率之间争取可靠性
-
-
1.3.5. UDP
-
1.3.5.1. UDP是一种简单的无连接协议,它将用户的程序暴露给不可靠的底层网络
-
1.3.5.2. 它无法保证消息传递按照规定的顺序进行,甚至消息传递可能根本不会发生
-
1.3.5.3. 可以被认为是底层IP协议之上的一层薄薄的单板(层),并在原始性能和可靠性之间争取性能
-
1.3.5.4. 非常适合许多现代应用程序,这些应用程序对异常丢失的数据包不敏感
1.3.5.4.1. 对于流媒体电影、视频会议和游戏,用户不太可能察觉到一个丢失的数据包
-
2. 远程方法调用
2.1. 使用直接与传输层协议TCP和UDP交互的底层API来编写分布式应用程序是完全可行的
2.2. 最常见的方法是调用标准化套接字库
- 2.2.1. 套接字库是一个底层的、难以使用的API
2.3. 套接字是客户端和服务器之间双向网络连接的一个端点
-
2.3.1. 套接字由节点IP地址和端口的组合来标识
-
2.3.2. 端口是唯一的数字标识符,它允许节点支持在该节点上运行的多个应用程序的通信
-
2.3.3. 每个IP地址都可以支持65535个TCP端口和另外65535个UDP端口
2.4. 可以直接向套接字API写入分布式应用程序,它是操作系统的核心组件
2.5. 注册表是一种简单的目录服务,通过它,客户端可以查找位置(网络地址和对象引用),并简单地提供逻辑名称来获取RMI服务器引用,逻辑名称在注册表中已经与服务器的引用相关联
2.6. 跨编程语言的编组(客户端使用一种编程语言,服务器端使用另一种编程语言)可能会导致错误,因为类型在不同语言中的表示方式不同,存在微妙的不兼容性
2.7. 多数现代系统都是围绕基于HTTP并使用JSON表示参数的更简单协议来构建的
- 2.7.1. HTTP动词(PUT、GET、POST等)具有映射到特定URL的关联语义,而不是操作名称
3. 局部故障
3.1. 分布式系统的组件通过网络通信
3.2. 异步网络特点
-
3.2.1. 节点可以随时向其他节点发送数据
-
3.2.2. 网络是半双工的,这意味着一个节点发送请求必须等待另一个节点的响应。涉及两个独立的通信
-
3.2.3. 节点之间传输数据的时长是变化的,受网络拥塞、动态数据包路由和瞬态网络连接故障等因素影响
-
3.2.4. 接收节点可能因软件或机器崩溃而无法使用
-
3.2.5. 数据可能会丢失
-
3.2.6. 节点没有完全一样的内部时钟;节点之间是不同步的
3.3. 同步网络则不一样,本质上是全双工的,同时在两个方向传输数据,每个节点的时钟是同步的
3.4. 客户端是否收到响应,以及何时收到响应,这被称为局部故障处理
3.5. 幂等性
-
3.5.1. 幂等操作执行多次的结果与执行一次的结果一致
-
3.5.2. 没有更改持久状态的请求自然是幂等的
- 3.5.2.1. 意味着所有读取请求本质上都是安全的,不需要在服务器上执行额外的工作
3.6. 更新持久状态则是另一回事
- 3.6.1. 系统需要设计一种机制,使得客户端重复的请求不会导致任何状态变化,并且可以被服务器检测到
3.7. 服务器状态发生变化的端点必须是幂等的
3.8. 构建幂等操作的方法
-
3.8.1. 客户端在所有改变状态的请求中包含一个唯一的幂等键
-
3.8.2. 服务器收到请求时,会检查它是否包含数据库中出现过的幂等性键值,而数据库是专门为实现幂等性而设计的
-
3.8.3. 如果幂等键在数据库中,表明此请求是来自客户端的重试,不应处理
3.9. 存储幂等键的数据库的实现
-
3.9.1. 单独的数据库表或集合,与应用程序数据一起存储在事务数据库中
-
3.9.2. 专用的数据库,提供极低查找延迟,例如简单的键值存储
3.10. 与应用程序数据不同,幂等键不必永远保留
-
3.10.1. 一旦客户端收到操作成功的确认,就可以丢弃幂等键了
-
3.10.2. 达成此目的最简单的方法是在特定时间段后自动从存储中删除幂等键,具体取决于应用程序需求和请求量
3.11. 幂等API实现必须确保应用程序状态已修改和幂等键已存储,两者均已发生API才能成功
3.12. 从本质上讲,事务确保了严格一次的操作(exactly-once semantics for operations),保证了所有消息始终只处理一次
-
3.12.1. 严格一次并不意味着没有消息传输失败、重试和应用程序崩溃,它们都是不可避免的
-
3.12.2. 重要的是重试最终会成功,结果总是一样的
-
3.12.3. 严格一次(exactly-once)消息传递需要防止重复,需要在可靠性和较慢的性能之间进行权衡
3.13. 最多一次(at-most-once)消息传递速度快且不可靠——这是UDP协议提供的
3.14. 至少一次(at-least-once)消息传递是TCP/IP提供的保证,意味着重复是不可避免的
4. 分布式系统中的共识
4.1. 事实上,不能保证一定会达成协议是可以证明的
4.2. 局部失败类似于丢失消息和确认
4.3. FLP不可能原理
-
4.3.1. 事实上,存在崩溃故障的异步网络上,消息可以延迟但不会丢失,在有限时间内不可能达成共识
-
4.3.2. FLP是最坏的情况
4.4. 在异步网络上无法保证在无限消息延迟的情况下达成共识
-
4.4.1. 实际上,分布式系统总是会达成共识
-
4.4.2. 尽管我们的网络是异步的,但我们可以在消息延迟上建立合理的界限并在超时后重试
4.5. 拜占庭故障,在分布式系统中尤为险恶
5. 分布式系统中的时间
5.1. 分布式系统中的每个节点都有自己的内部时钟
-
5.1.1. 如果每台机器上的时钟都是完全同步的,我们就可以简单地比较节点间事件的时间戳,确定它们发生的精确顺序
-
5.1.2. 受温度或电压变化等环境条件影响,各个节点上的时钟会漂移
-
5.1.3. 每台机器的时间漂移量各不相同,但每天漂移10~20 s的现象并不少见
5.2. 时间服务是准确的时间源
5.3. 使用最广泛的时间服务是NTP(网络时间协议)
-
5.3.1. 提供了跨越全球的分层组织时间服务器集合
-
5.3.2. 全球约有300台根服务器,它们的时间是最准确的
-
5.3.3. 下一级时间服务器(大约20000个)定期(几毫秒内)与根服务器同步
-
5.3.4. 整个层次结构最多15级
-
5.3.5. 全球有超过175000台NTP服务器
-
5.3.6. 节点上的时间由与一个或多个NTP服务器进行的UDP消息交换来同步
- 5.3.6.1. 消息带有时间戳,通过消息交换估算消息传输所用的时间
-
5.3.7. 时钟的重置可以将本地节点时间向前或向后移动
- 5.3.7.1. 如果我们的应用程序正在计算事件所花费的时间(例如,计算事件响应时间),如果NTP协议已设置本地时间并导致时间倒退,则事件的结束时间可能早于开始时间
5.4. 日历钟
- 5.4.1. 日历钟(time of day clock)是自1970年1月1日午夜以来的毫秒数
5.5. 单调时钟
- 5.5.1. 单调时钟(monotonic clock)是自过去未指定时间点以来的时间量(以秒和纳秒为单位)
5.6. 应用程序可以使用NTP服务来确保系统中每个节点上的时钟紧密同步
-
5.6.1. 通常会以一小时到一天的时间间隔重新同步时钟,确保时钟的时间值保持接近
-
5.6.2. 如果应用程序确实需要准确了解不同节点上事件发生的顺序,那么时钟漂移将使其充满危险
-
5.6.3. 我们的应用程序不能依赖不同节点上事件的时间戳来表示事件的实际顺序
- 5.6.3.1. 即使时钟漂移一两秒也会导致跨节点时间戳无法比较
5.7. Chrony支持NTP协议,但比NTP准确性更高和扩展性更好
6. 要点
6.1. 解决方案分布在不同位置的多台机器上,每台机器并行处理事件,且它们之间通过网络交换消息
6.2. 分布式系统中的通信可以透明地穿过许多不同类型的底层物理网络,包括WiFi、无线网络、广域网和局域网
6.3. 互联网协议栈通过IP和TCP协议的组合确保跨异构网络的可靠通信
6.4. 使用RMI/RPC技术构建TCP/IP层,为客户端/服务器通信提供抽象层,采用本地方法/过程调用的方式调用服务器接口
6.5. 在异步网络中,存在崩溃故障的情况下,有限时间内不可能在多个节点之间就状态达成一致或达成共识
6.6. 没有完全可靠的全局时间源可供应用程序中的节点同步其行为
-
6.6.1. 各个节点上的时钟各不相同,不能用于精确的比较
-
6.6.2. 应用程序无法通过比较不同节点上的时钟来确定事件的顺序