高可用性思考

midoll 261 2022-07-04

高可用性

High Availability HA 是指系统具备较高的无故障运行的能力。

可用性的度量

可用性是一个抽象的概念,如何度量它,与之相关的概念是:MTBF和MTTR

  • MTBF(Mean Time Between Failure)是平均故障间隔的意思,代表两次故障的间隔时间,也就是系统正常运转的平均时间,这个时间越长,系统稳定性越高。
  • MTTR(Mean Time To Repair)表示故障的平均恢复时间,也可以理解为平均故障时间,这个值越小,故障对于用户的影响越小。
    Availability = MTBF/(MTBF+MTTR)
    image
    三个九之后,系统的年故障时间从 3 天锐减到 8 小时。到了四个九之后,年故障时间缩减 到 1 小时之内。在这个级别的可用性下,你可能需要建立完善的运维值班体系、故障处理 流程和业务变更流程。你可能还需要在系统设计上有更多的考虑。比如,在开发中你要考 虑,如果发生故障,是否不用人工介入就能自动恢复。当然了,在工具建设方面,你也需要 多加完善,以便快速排查故障原因,让系统快速恢复。
    到达五个九之后,故障就不能靠人力恢复了。想象一下,从故障发生到你接收报警,再到你
    打开电脑登录服务器处理问题,时间可能早就过了十分钟了。所以这个级别的可用性考察的
    是系统的容灾和自动恢复的能力,让机器来处理故障,才会让可用性指标提升一个档次。
    一般来说,我们的核心业务系统的可用性,需要达到四个九,非核心系统的可用性最多容忍 到三个九。在实际工作中,你可能听到过类似的􏰀说法,只是不同级别,不同业务场景的系 统对于可用性要求是不一样的。

高可用系统的设计思路

一个成熟的系统的可用性需要从系统设计和系统运维两方面来保障,两者共同作用,缺一不可。

1.系统设计

Design for failure 是高可用系统设计时秉持的第一原则。在承担QPS几百万的高并发中,集群中机器的数量成百上千台,单机的故障几乎是常态,几乎每一天都会发生。
未雨绸缪才能决胜千里,因此在做系统设计时,要把发生故障作为一个重要的考虑点,预先考虑如何自动化地发现故障,发生故障之后要如何解决。譬如failover(故障转移),超时控制、降级和限流等。
一般来说failover发生的节点可能有两种:
1.在完全对等的节点之间做failover
2.在不对等的节点之间,即系统中存在主节点也存在备用节点。

  • 在对等节点之间做failover相对简单写,在这类系统中所有节点都承担读写流量,并且节点中不保存状态,每个节点都可以作为另一个节点的镜像,这种情况下,如果访问一个节点失败,那么简单地随机访问另一个节点就好了

  • 在不对等节点的failover机制会复杂很多,比如我们有一个主节点,有多台备用节点,这些备用节点可以是热备(同样在线提供服务的备用节点),也可以是冷备(只作为备份使用),那么就需要在代码中控制如何监测主备机器是否故障,以及如何做主备切换。
    使用最广泛的故障监测机制就是心跳,你可以在客户端上定期向主节点发送心跳包,也可以从备份节点上定时发送心跳包,当一段时间内未收到心跳包,就可以认为主节点已经发生故障,可以触发选主的操作(策略)。
    选主的结果要在多个备份节点上达成一致,所以会使用一种分布式一致性算法,譬如Paxos,Raft
    除了故障转义外,对于系统间调用超时的控制也是高可用系统设计的一个重要方面。

  • 复杂的高并发系统通常会有很多的系统模块组成,同时也会依赖很多的组件和服务,譬如说缓存组件,队列服务等,他们之间的调用最怕的就是延迟而非失败,因为失败通常是瞬时的,可以通过重试的方法解决。而一旦调用某一个模块或者服务发生比较大的延迟,调用方就会阻塞在这次调用上,它已经占用的资源得不到释放,当存在大量这种阻塞请求时,调用方就会因用尽资源而挂掉。

  • 在系统开发的初期,超时控制通常不被重视,或者是没有方式来确定正确的超时时间。
    既然要做超时控制,那么怎么来确定超时时间呢?good question
    超时时间短了,会造成大量的超时错误,对用户体验产生影响;超时时间长了,又起不到作用。我建议你通过收集系统之间的调用日志,统计比如99%的响应时间是怎样的,然后依据这个时间来指定超时时间。另一种是按照经验值来指定超时时间,不过无论使用那种方式,超时时间都不是一成不变的,需要在后面的系统维护过程中不断地修改。

  • 超时控制实际上就是不让请求一直保持,而是在经过一定时间之后让请求失败,释放资源给接下来的请求使用。这对于用户来说是有损的,但是却是必要的,因为它牺牲了少量的请求却保证了整体系统的可用性。而我们还有其他另外两种有损的方案保证系统的高可用,他们就是降级和限流。

  • 降级是为了保证核心服务的稳定而牺牲非核心服务的做法。比方说我们下一个订单,会先经过反作弊服务检测,通过后才会完成诸如写数据库等逻辑。
    反作弊的检测是一个相对比较重的操作,因为涉及到非常多的策略匹配,在正常情况下虽然比较耗时却还能正常响应。如果是并发比较高的情况下,它就可能成为瓶颈,而且它不属于下单的主体流程,所以我们可以暂时关闭服务,保证主体流程更加稳定。

  • 限流完全是另外一种思路,它通过对并发的请求进行限速来保护系统。
    对于web应用,限制单机只能处理每秒1000次请求,超过的部分直接返回错误给客户端。虽然损害了用户的使用体验,但是它是极端并发下的无奈之举,是短暂的行为,因此可以接受。

  • 1.系统心跳设计?
    选主的结果需要再多个备份节点上达成一致,所以会使用一种分布式一致性算法,Paxos,Raft
    有损方案来保证系统的高可用:1、超时设置 2、降级 3、限流

2.系统运维

  • 在系统运维的层面可以做什么呢,可以从灰度发布、故障演练两个方面来考虑如何提升系统的可用性。
    在业务平稳运行过程中,系统是很少发生故障的,90%的故障是发生在上线变更阶段。比如说,你上一个新的饿功能,由于设计方案的问题,数据库的慢请求数翻了一倍,导致系统请求被拖慢而产生故障。
    为了提升系统的可用性,重视变更管理尤为重要,而除了提供必要的回滚方案,以便在出现问题时快速回滚恢复之外,另一个主要的手段就是灰度发布。

  • 灰度发布是在系统正常运行条件下,保证系统高可用的运维手段,那如何知道发生故障时系统的表现呢, 这就需要依靠另一个手段:故障演练。

  • 故障演练指的是对系统进行一些列破坏性的手段,观察在出现局部故障时,整体的系统表现是怎么样的,从而发现系统中存在的、潜在的可用性问题。
    故障演练和时下比较流行的“混沌工程”的思路如出一辙,作为混沌工程的鼻祖,Netfix2010年推出的“Chaos Monkey” 工具就是故障演练绝佳的工具,它通过在线上系统上随机地关闭线上节点来模拟故障,让工程师可以了解,在出现此类型故障时会有什么样的影响。当然,这一切是以你的系统可以抵御一些异常情况为前提的,如果你的系统还没有做到这一点,建议你另外搭建一套和线上部署结构一摸一样的线下系统,然后在这套系统上做故障演练,从而避免对生产系统造成影响。

  • 提高系统的可用性有时候是以牺牲用户体验或者牺牲系统性能为前提的,也需要大量人力来建设相应的系统,完善机制。所以需要我们把握一个度,不该做过度的优化。

  • 有没有系统不追求性能,只追求极致的可用性呢?是有的,比如配置下发的系统,它只需要在其他系统启动时提供一份配置即可,所以可以是秒级返回,几十秒也OK,无非是增加了其他系统的启动速度而已,但是 它对可用性的要求是极高的,甚至要求6个9,配置可以获取的慢,但是不能获取不到。因此,在实际生产中,可用性和性能有时候是需要做取舍的,视不同的系统而定。


# spring cloud