架构设计的复杂度来源其实就是架构设计要解决的问题,主要有如下几个:高性能、高可用、可扩展、低成本、安全、规模。复杂度的关键,就是新旧技术之间不是完全的替代关系,有交叉,有各自的特点,所以才需要具体问题具体分析,基于各方考虑设计合适的架构,存在合适的架构,不存在最好的架构。这篇主要讨论高可用问题
复杂度来源
什么是高可用:高可用指系统无中断地执行其功能的能力,代表系统的可用性程度,是进行系统设计时的准则之一。关键在于无中断,但恰好难点也在无中断上面,无论是单个硬件还是单个软件,都不可能做到无中断,硬件会出故障,软件会有 bug;硬件会逐渐老化,软件会越来越复杂和庞大,同时外部环境导致的不可用更加不可避免、不受控制
所以,系统的高可用方案五花八门,但万变不离其宗,本质上都是通过冗余来实现高可用,单纯从形式上来看,和高性能是一样的,都是通过增加更多机器来达到目的,但其实本质上是有根本区别的:高性能增加机器目的在于“扩展”处理性能(为了横向扩展,支撑更高的系统压力);高可用增加机器目的在于“冗余”处理单元(为了规避单机器/机房/城市可能出现的不可抗力等的因素,从而造成的服务中断)
冗余增强了可用性,但同时也带来了复杂性
计算高可用
这里的计算指的是业务的逻辑处理,计算高可用的复杂度体现在集群的整理管理,与高性能类似
- 需要增加一个任务分配器,选择合适的任务分配器也是一件复杂的事情,需要综合考虑性能、成本、可维护性、可用性等各方面因素。
- 任务分配器和真正的业务服务器之间有连接和交互,需要选择合适的连接方式,并且对连接进行管理。例如,连接建立、连接检测、连接中断后如何处理等。
- 任务分配器需要增加分配算法。例如,常见的算法有主备、主主,主备方案又可以细分为冷备、温备、热备。
这里的侧重点其实是说任务分配器对于计算服务器的故障管理,例如计算服务器3主3备,平时主提供读写服务,备不提供服务。主挂了,备接替主提供服务。如果从高性能角度出发,任务分配器侧重的是对于计算服务器的流量的水平分配
冷备:系统没启动;温备:系统启动,但是没法接管业务;热备:系统启动,随时可以接管业务。
集群的分配算法更加复杂,可以是 1 主 3 备、2 主 2 备、3 主 1 备、4 主 0 备。例如ZooKeeper 和 Memcached 在数据存储和备份策略上有所不同:
-
ZooKeeper: ZooKeeper 采用的是一主多备(1 主多备)的架构。这意味着在一个 ZooKeeper 集群中,有一个主节点负责处理客户端的读写请求,同时有多个备份节点(通常是奇数个)作为备用,以提供高可用性和容错能力。如果主节点出现故障,备份节点可以接替成为主节点继续提供服务。
-
Memcached: Memcached 采用的是全主 0 备的模式。这意味着在 Memcached 的设计中,没有数据备份或者冗余,所有的数据都只存储在主服务器上。这样的设计简化了系统的复杂性,但也意味着如果主服务器发生故障,可能会导致数据丢失或不可用性。
这两种设计在数据一致性、可用性和可靠性上有不同的权衡,适用于不同的应用场景和需求。
存储高可用
存储与计算相比,有一个本质上的区别:将数据从一台机器搬到到另一台机器,需要经过线路进行传输,按照数据 + 逻辑 = 业务这个公式来套的话,数据不一致,即使逻辑一致,最后的业务表现就不一样了。
无论是正常情况下的传输延迟,还是异常情况下的传输中断,都会导致系统的数据在某个时间点或者时间段是不一致的,而数据的不一致又会导致业务问题;但如果完全不做冗余,系统的整体高可用又无法保证,所以存储高可用的难点不在于如何备份数据,而在于如何减少或者规避数据不一致对业务造成的影响
高可用状态决策
无论是计算高可用还是存储高可用,其基础都是“状态决策”,即系统需要能够判断当前的状态是正常还是异常,如果出现了异常就要采取行动来保证高可用,计算高可用:如果异常了,那么就不负载到这个异常计算服务器上了; 存储高可用:如果异常了,使用备用存储服务器
独裁式
独裁式决策指的是只存在一个独立的决策主体,优点是决策方式不会出现决策混乱的问题,因为只有一个决策者,缺点也正是在于只有一个决策者。当决策者本身故障时,整个系统就无法实现准确的状态决策
协商式
协商式决策指的是两个独立的个体通过交流信息,然后根据规则进行决策,最常用的协商式决策就是主备决策,机器之间通过信息交换协商谁为主谁为备
协商式决策的架构不复杂,规则也不复杂,其难点在于,如果两者的信息交换出现问题(比如主备连接中断),此时状态决策应该怎么做
- 如果备机在连接中断的情况下认为主机故障,那么备机需要升级为主机,但实际上此时主机并没有故障,那么系统就出现了两个主机
- 如果备机在连接中断的情况下不认为主机故障,则此时如果主机真的发生故障,那么系统就没有主机了
- 如果为了规避连接中断对状态决策带来的影响,可以增加更多的连接。例如,双连接、三连接。这样虽然能够降低连接中断对状态带来的影响(注意:只能降低,不能彻底解决),但同时又引入了这几条连接之间信息取舍的问题,即如果不同连接传递的信息不同,应该以哪个连接为准
协商式状态决策在某些场景总是存在一些问题的
民主式
民主式决策指的是多个独立的个体通过投票的方式来进行状态决策。例如,ZooKeeper 集群在选举 leader 时就是采用这种方式
民主式决策和协商式决策比较类似,其基础都是独立的个体之间交换信息,每个个体做出自己的决策,然后按照“多数取胜”的规则来确定最终的状态。但是这个决策算法很复杂,而且还有可能出现脑裂的问题
总结一下
核心思想:网站高可用的主要技术手段是服务与数据的冗余备份与失效转移。同一服务组件部署在多台服务器上;数据存储在多台服务器上互相备份。通过上述技术手段,当任何一台服务器宕机或出现各种不可预期的问题时,就将相应的服务切换到其他可用的服务器上,不影响系统的整体可用性,也不会导致数据丢失。
从架构角度看可用性:当前网站系统多采用经典的分层模型,从上到下为:应用层、服务层与数据层。应用层主要实现业务逻辑处理;服务层提供可复用的服务;数据层负责数据读写;在部署架构上常采用应用和数据分离部署,应用会部署到不同服务器上,这些服务器被称为应用层的服务器;这些可复用的服务也会各自部署在不同服务器上,称为服务层的服务器;而各类数据库系统、文件柜等数据则部署在数据层的服务器。
硬件故障方面引起不可用的技术解决措施:
- 应用服务器。可通过负载均衡设备将多个应用服务器构建为集群对外提供服务(前提是这些服务需要设计为无状态,即应用服务器不保存业务的上下文信息,而仅根据每次请求提交的数据进行业务逻辑的操作响应),当均衡设备通过心跳检测手段检测到应用服务器不可用时,则将其从集群中移除,并将请求切换到其他可用的应用服务上。
- 服务层服务器。这些服务器被应用层通过分布式服务框架(如Dubbo)访问,分布式服务框架可在应用层客户端程序中实现软件负载均衡,并通过服务注册中心提供服务的服务器进行心跳检测,当发现有服务器不可用时,立即通知客户端程序修改服务列表,同时移除未响应的服务器。
- 数据服务器。需要在数据写入时进行数据同步复制,将数据写入多台服务器上,实现数据冗余备份;当数据服务器宕机时,应用程序将访问切换到有备份数据的服务器上。