计算高可用:
- 设计目标:当出现部分硬件损坏时,计算任务能够继续正常运行。
- 本质:通过冗余来规避部分故障的风险,单台服务器是无论如何都达不到这个目标的。
- 设计复杂度:在任务管理方面,即当任务在某台服务器上执行失败后,如何将任务重新分配到新的服务器进行执行。
所以计算高可用的设计思想很简单:通过增加更多服务器来达到计算高可用。
计算高可用架构设计的关键点有下面两点:
- 哪些服务器可以执行任务:
- 与高性能架构类似,每个服务器都可以执行任务
- 与高可用存储架构类似,只有特定服务器(主)可以执行任务,当执行任务的服务器故障后,系统需要挑选新的服务器来执行任务。
- 任务如何重新执行:
- 对于已经分配的任务即使执行失败也不做任务处理,系统只要保证新的任务能够分配到非故障服务器上执行即可
- 设计一个任务管理器来管理需要执行的计算任务,服务器执行完任务后,向任务管理器反馈结果,任务管理器根据任务结果来决定是否需要将任务重新分配到另外的服务器上执行。
“任务分配器”是一个逻辑的概念,并不一定要求系统存在一个独立的任务分配器模块。例如:
- Nginx 将页面请求发送给 Web 服务器,而
CSS/JS
等静态文件直接读取本地缓存。此时 Nginx 是反向代理又承担任务分配器的职责。 - 后台批量运算的任务,可以设计独立的任务分配器来管理这些批处理任务的执行和分配。
- ZooKeeper 中的 Follower 节点,当接收到写请求时会将请求转发给 Leader 节点处理,当接收到读请求时就自己处理,此时 Follower 就相当于一个逻辑上的任务分配器。
0.1. 主备
主备架构是计算高可用最简单的架构,和存储高可用的主备复制架构类似,但更简单无须数据复制。
- 主机执行所有计算任务。例如,读写数据、执行操作等。
- 当主机故障(例如,主机宕机)时,任务分配器不会自动将计算任务发送给备机,此时系统处于不可用状态。
- 如果主机能够恢复(人工/自动),任务分配器继续将任务发送给主机。
- 如果主机不能够恢复(例如,硬件损坏),则需要人工操作,将备机升为主机,然后让任务分配器将任务发送给新的主机;同时,为了继续保持主备架构,需要人工增加新的机器作为备机。
根据备机状态的不同,主备架构又可以细分为冷备架构和温备架构。
冷备可以节省一定的能源,但温备能够大大减少手工操作时间,因此一般情况下推荐用温备的方式。
主备架构:
- 优点:简单,主备之间不需进行交互,状态判断和切换操作由人工执行,系统实现很简单。
- 缺点:人工操作的时间不可控,人工操作的效率低,人工操作过程容易出错。
和存储高可用中的主备复制架构类似,计算高可用的主备架构也比较适合与内部管理系统、后台管理系统这类使用人数不多、使用频率不高的业务,不太适合在线的业务。
0.2. 主从
和存储高可用中的主从复制架构类似,计算高可用的主从架构中的从机也是要执行任务的。任务分配器需要将任务进行分类,确定哪些任务可以发送给主机执行,哪些任务可以发送给备机执行。
- 正常情况下,主机执行部分计算任务,备机执行部分计算任务。
- 当主机故障(例如,主机宕机)时,任务分配器不会自动将原本发送给主机的任务发送给从机,而是继续发送给主机,不管这些任务执行是否成功。
- 如果主机能够恢复(人工/自动),任务分配器继续按照原有的设计策略分配任务。
- 如果主机不能够恢复(例如,硬件损坏),则需要人工操作,将原来的从机升级为主机(一般只是修改配置即可),增加新的机器作为从机,新的从机准备就绪后,任务分配器继续按照原有的设计策略分配任务。
主从架构与主备架构相比:
- 优点:主从架构中从机也执行任务,发挥了从机的硬件性能。
- 缺点:主从架构中需要将任务分类,任务分配器会复杂一些。
0.3. 集群
主备架构和主从架构通过冗余一台服务器来提升可用性,且需要人工切换主备或主从,架构虽简单,但存在人工操作效率低、容易出错、不能及时处理故障等问题。
在可用性要求更加严格的场景中,需要系统能够自动完成切换操作,这就是高可用集群方案。
根据集群中服务器节点角色的不同,可以分为两类:
- 对称集群,集群中每个服务器的角色都是一样的,都可以执行所有任务;
- 非对称集群,集群中的服务器分为多个不同的角色(例如,主从角色),不同的角色执行不同的任务。
注意:
- 计算高可用集群:包含 2 台服务器的集群,这与多台服务器的集群,在设计上没有本质区别
- 存储高可用集群:把双机架构和集群架构进行了区分
0.3.1. 对称集群
也称为负载均衡集群。
- 正常情况下,任务分配器采取某种策略(随机、轮询等)将计算任务分配给集群中的不同服务器。
- 当集群中的某台服务器故障后,任务分配器不再将任务分配给它,而是将任务分配给其他服务器执行。
- 当故障的服务器恢复后,任务分配器重新将任务分配给它执行。
负载均衡集群的设计关键点在于两点:
- 任务分配器需要选取分配策略,轮询和随机基本就够了。
- 任务分配器需要检测服务器状态。既要检测服务器的状态(例如,服务器是否宕机、网络是否正常等);同时要检测任务的执行状态(例如,任务是否卡死、是否执行时间过长等)。常用的做法是任务分配器和服务器之间通过心跳来传递信息,包括服务器信息和任务信息,然后根据实际情况来确定状态判断条件。
例如,一个在线页面访问系统,正常情况下页面平均会在 500 毫秒内返回,那么状态判断条件可以设计为:1 分钟内响应时间超过 1 秒(包括超时)的页面数量占了 80% 时,就认为服务器有故障。
例如,一个后台统计任务系统,正常情况下任务会在 5 分钟内执行完成,那么状态判断条件可以设计为:单个任务执行时间超过 10 分钟还没有结束,就认为服务器有故障。
不同业务场景的状态判断条件差异很大,实际设计时要根据业务需求来进行设计和调优。
0.3.2. 非对称集群
非对称集群中不同服务器的角色是不同的,不同角色的服务器承担不同的职责。
以 Master-Slave 为例,部分任务是 Master 服务器才能执行,部分任务是 Slave 服务器才能执行。
- 集群会通过某种方式来区分不同服务器的角色。例如,通过 ZAB 算法选举,或者简单地取当前存活服务器中节点 ID 最小的服务器作为 Master 服务器。
- 任务分配器将不同任务(对任务进行分类)发送给不同服务器。
- 当指定类型的服务器故障时,需要重新分配角色。
- 如果是 Master 服务器故障,需要将剩余的 Slave 服务器中的一个重新指定为 Master 服务器;
- 如果是 Slave 服务器故障,不需要重新分配角色,只需将故障服务器从集群剔除即可。
非对称集群相比负载均衡集群,设计复杂度主要体现在两个方面:
- 任务分配策略复杂:将任务划分为不同类型并分配给不同角色的集群节点。
- 角色分配策略复杂:使用 ZAB、Raft 这类复杂的算法来实现 Leader 的选举。
以 ZooKeeper 为例:
- 任务分配:ZooKeeper 中不存在独立的任务分配器节点,每个节点都是任务分配器,Follower 收到请求后会进行判断,如果是写请求就转发给 Leader,如果是读请求就自己处理。
- 角色指定:ZooKeeper 通过 ZAB 算法来选举 Leader,当 Leader 故障后,所有的 Follower 节点会暂停读写操作,开始进行选举,直到新的 Leader 选举出来后才继续对 Client 提供服务。