软件架构之高可用性设计( 二 )


我们在每一个逻辑层,都可以增加冗余 。如果其中一个服务挂了,可以通过自动的故障转移(fail over),让其他服务点继续提供服务 。通过缓存数据的冗余实现的,常见实践是缓存客户端双读双写,或者利用缓存集群的主从数据同步与sentinel保活与自动故障转移;更多的业务场景,对缓存没有高可用要求,可以使用缓存服务化来对调用方屏蔽底层复杂性
数据库层的冗余可以采用”主从同步,读写分离”的架构,可以把它分为“读库高可用”和“写库高可用”两类 。“读库高可用”的常见实践是通过db-connection-pool来保证自动故障转移,“写库高可用”则是通过写库的冗余实现的,常见实践是keepalived + virtual IP自动故障转移 。
文件存储部分,对于上层文件系统而言,逻辑上是一个整体,我们可以使用分布式文件系统,利用分布式集群,提供对文件系统的支持,对外提供统一的命名空间,文件系统内部也实现了各种冗余和负载均衡操作,有比较好的容错性,提高了文件系统的可用性 。
分布式文件系统广泛流行前,对存盘数据的高可靠性,可以引入RAID(独立冗余磁盘阵列) 。RAID 技术将多个单独的物理硬盘以不同的方式组合成一个逻辑硬盘,同一份数据会以一定组合方式,写入多块磁盘 。即使其中部分磁盘损坏,仍然可以保证数据的可用性 。
2. 无状态设计
要想服务能够切换,做故障转移(fail over或fail back),很重要的一点就是不要保存业务的上下文信息,而仅根据每次请求提交的数据进行相应的逻辑处理,这样的话,多个服务实例之间就是完全对等的,请求提交到任意服务器,处理的结果是完全一样的 。在这个情况下,我们就可以利用负载均衡进行无状态服务的失效转移 。
但是我们总要管理session信息,这些信息可以下沉到缓存层和数据库层 。这样服务层就可以根据需要做到动态的Scale-out/Scale-In和高可用了 。

软件架构之高可用性设计

文章插图
 
3. 负载均衡
上文谈到的几点,都可以看到负载均衡的影子,大型系统的网关层基本都会引入负载均衡,负载均衡可以提高系统并发性支持,反过来说减轻了单一服务的压力,提高了系统的可用性,通常会有两种形式:
· 硬件负载—F5 7层或4层网络代理
· 软件负载—Nginx, Haproxy等开源的负载均衡软件 。
常用的算法通常有
1. 轮询法
2. 随机法
3. 源地址哈希法
4. 加权轮询法
5. 加权随机法
6. 最小连接数法
软件架构之高可用性设计

文章插图
 
4.幂等设计
什么是幂等性呢,简而言之就是同样的请求,即使多次重复,也必须得到相同的结果 。
幂等性在支付类场景尤为重要,比如重复的的订单号,同样的金额,不做幂等性设计,重复支付可能就会造成金额累加 。
对于服务幂等性设计的要点就是一定要效验请求参数有效性,及已有数据的对比 。如果同样的请求参数已经处理过就不要重复处理,直接返回,这就是幂等性核心点 。
5. 超时机制
我们的服务,尤其是微服务化的场景下,通过REST API进行相互调用,为了保证系统的可用性,就需要对调用设置超时机制,一旦被调用服务超时还未返回,主调服务就应该进入超时处理流程 。这样就不至于在某个服务不可用时,整个系统进入阻塞状态 。
6.异步调用
采用异步调用方式调用被调用的服务,有助于将主调服务和被调服务解耦,减少等待时被阻塞的情况,并能提高系统的并发性能 。对于不需要关心直接返回结果的请求类型以及当请求流程不是系统的关键路径时,采用异步化是非常有价值的 。
7.服务分级与降级
在一个大系统中,一般会有核心服务和次要服务之分,那么对于不同的服务可以采用不同的处理方案,出现故障时应该优先保证核心服务的运行,对于次要的服务,可以延迟服务或在粒度范围内关闭服务 。服务降级一般是关注业务,对业务进行考虑,抓住业务的层级,从而决定在哪一层上进行处理:比如在IO层,业务逻辑层,还是在外围进行处理 。
8.服务熔断
服务熔断也叫服务的过载保护,服务熔断对服务提供了proxy,防止服务不可能时,出现串联故障(cascading failure),导致雪崩效应 。服务熔断一般是某个服务(下游服务)故障引起,而服务降级一般是从整体负荷考虑;对于设计的任何一个系统,都需要进行容量的预估和最大容量设置,当外部请求量超过最大QPS容量时,应该启动防雪崩机制,以避免大量外部请求把服务压跨而不能对外提供服务 。


推荐阅读