大厂都是如何对高并发系统做到高可用的?

Posted JavaEdge.

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了大厂都是如何对高并发系统做到高可用的?相关的知识,希望对你有一定的参考价值。

高可用性(High Availability,HA)

系统具备较高的无故障运行的能力。

Hadoop1.0的NameNode是单点,一旦故障,整个集群不可用。Hadoop2提出的NameNode HA方案就是同时启动两个NameNode:

  • 一个处Active状态
  • 另一个处Standby状态

两者共享存储,一旦Active NameNode故障,可将Standby NameNode切换成Active状态继续服务,增强了Hadoop持续无故障运行能力,即提升可用性。

可用性度量:

MTBF(Mean Time Between Failure)

平均故障间隔,代表两次故障的间隔时间,也就是系统正常运转的平均时间。这个时间越长,系统稳定性越高。

MTTR(Mean Time To Repair)

故障的平均恢复时间,也可以理解为平均故障时间。值越小,故障对于用户的影响越小。

可用性与MTBF和MTTR的值息息相关,公式关系:

Availability = MTBF / (MTBF + MTTR)

这个公式计算出的结果是个比例,代表系统可用性。一般会使用几个9描述系统可用性:

三个9后,系统的年故障时间从3d锐减到8h。到了四个九之后,年故障时间缩减到1小时之内。在这个级别的可用性下,你可能需要建立完善的运维值班体系、故障处理流程和业务变更流程。
还需要在系统设计上有更多的考虑。比如,在开发中你要考虑,如果发生故障,是否不用人工介入就能自动恢复。
工具建设也需更完善,以便快速排查故障原因,让系统快速恢复。

五个9后,故障就不能靠人力恢复。这个级别的可用性考察的是系统的容灾和自动恢复的能力,让机器来处理故障,才会让可用性指标提升一个档次。

核心业务系统的可用性,需要达到四个九,非核心系统可用性最多容忍三个九。

2 高可用系统设计

2.1 系统设计

“Design for failure”,高可用系统设计时秉持的第一原则。在承担百万QPS的高并发系统中,集群中机器的数量成百上千台,单机的故障是常态,几乎每一天都有发生故障的可能。

系统设计时,要把发生故障作为重要考虑点,预先考虑如何自动化发现故障,故障后如何解决。
还需掌握具体优化方法,如failover(故障转移)、超时控制、降级和限流。

发生failover的节点可能:

  • 在完全对等的节点之间做failover
  • 在不对等的节点之间,即系统中存在主、备节点

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

比如nginx可以配置当某一个Tomcat出现>500的请求时,重试请求另一个Tomcat节点:

针对不对等节点的failover机制会复杂很多。比方有一个主节点,多台备用节点,这些备用节点可以是热备(同样在线提供服务的备用节点),也可以是冷备(只作为备份使用),那么我们就需要在代码中控制如何检测主备机器是否故障,以及如何做主备切换。

2.2 故障检测机制 - 心跳

可在客户端上定期地向主节点发送心跳包,也可在从备份节点上定期发送心跳包。当一段时间内未收到心跳包,就可以认为主节点已经发生故障,触发选主。

选主结果需在多个备份节点达成一致,所以会使用分布式一致性算法Paxos,Raft。

2.3 系统间调用超时

高并发系统通常有很多系统模块组成,依赖很多组件服务,如缓存组件,队列服务。调用最怕延迟,因为失败通常瞬时,可重试解决。而一旦调用某模块或服务发生较大延迟,调用方就会阻塞在这次调用,它已占用的资源无法释放。当存在大量这种阻塞请求,调用方就会因为用尽资源而宕机。

系统开发初期,超时控制通常不重视或未设置合适超时时间。

某项目的模块间RPC调用,超时时间默认30s。平时系统运行稳定,但流量大时,RPC服务端出现一定数量慢请求时,RPC客户端线程就会大量阻塞在这些慢请求上长达30s,造成RPC客户端用尽调用线程而宕机。

确定超时时间

  • 超时时间过短
    会造成大量超时错误,对用户体验产生影响
  • 超时时间过长
    又起不到作用

推荐收集系统之间的调用日志,统计比如99%响应时间是怎样的,然后依据该时间指定超时时间。
若无调用日志,则只能据经验指定超时时间。
无论使用何方式,超时时间都不会一直不变,系统迭代维护过程中会不断修改。

超时控制实际上就是不让请求一直保持,而是在经过一定时间之后让请求失败,释放资源给其它请求使用。
这对于用户有损,但是必要的,牺牲少量请求却保证了系统可用性。

还有两种有损方案保证系统高可用:

2.4 降级

为保证核心服务的稳定而牺牲非核心服务。

比方发条微博会先经过反垃圾服务检测,检测内容是否是广告,通过后才完成诸如写DB逻辑。

反垃圾检测是相对较重操作,涉及很多策略匹配,在日常流量下虽然会比较耗时却还能正常响应。
但并发较高时,它可能成为瓶颈,而且它也不是发微博的主体流程,可暂时关闭,这就能保证主体的流程稳定。

2.5 限流

通过对并发请求进行限速保护系统。

比如对Web应用,限制单机只能处理每s 1000次请求,超过部分直接返回错误给客户端。
这种做法虽损害用户体验,但是在极端并发下的无奈之举,属短暂行为可接受。

3 系统运维

可从灰度发布、故障演练考虑提升系统可用性。

在业务平稳运行过程中,系统很少故障,90%的故障发生在上线变更阶段。
比如你上了个新功能,由于设计方案问题,DB的慢请求数翻了一倍,导致系统请求被拖慢而产生故障。

若未变更,DB怎会无缘无故产生那么多慢请求?
重视变更管理很重要。除了提供回滚方案,另一个方案就是灰度发布。

灰度发布

系统的变更不是一次性推到线上,而是按照一定比例逐步推进。
一般灰度发布以机器维度进行。比如先在10%机器变更,同时观察系统性能指标及错误日志。
若观察一段时间后,系统指标依旧平稳且无大量错误日志,再推动全量变更。

所以灰度发布给了开发和运维同学转运机会,能在线上流量观察变更的影响,这是保证系统高可用的关键流程。

灰度发布是在系统正常运行条件下,保证系统高可用的运维手段,那如何知道发生故障时的系统表现?

故障演练

对系统进行一些破坏性的手段,观察在出现局部故障时,整体的系统表现是怎样的,从而发现系统中存在的,潜在的可用性问题。

比如磁盘,DB,网卡等组件随时随地可能故障,一旦故障,会不会如蝴蝶效应造成整体服务不可用呢?我们也无法知道,因此,必须故障演练。
和“混沌工程”的思路类似,作为混沌工程鼻祖,Netfix在2010年推出的“Chaos Monkey”工具就是故障演练绝佳的工具。它通过在线上系统上随机地关闭线上节点模拟故障,让工程师能够知晓在出现此类故障时会有何种影响。

这一切是以你的系统可抵御一些异常情况为前提。
若你的系统还没做到这点,建议你另外搭建一套和线上部署结构一模一样的线下系统,然后在这套系统上做故障演练,从而避免影响生产系统。

4 总结

开发注重的是如何处理故障,关键词是冗余和取舍

  • 冗余指的是有备用节点,集群来顶替出故障的服务,比如文中提到的故障转移,还有多活架构等等
  • 取舍指的是丢卒保车,保障主体服务安全

从运维角度来看则更偏保守,注重的是如何避免故障的发生,比如更关注变更管理以及如何做故障的演练。

两者结合起来才能组成一套完善的高可用体系。

提高系统的可用性,有时是以牺牲用户体验或系统性能为前提,也需大量人力才能建设完善。所以不该优化。比如核心系统四个九的可用性已经可满足需求,不必盲目追求更高可用性了。

有没有不追求性能,只追求极致可用性的呢?
有的。比如配置下发的系统,只需在其它系统启动时提供一份配置,所以秒级返回也可,十秒钟也OK,无非就是增加其它系统的启动时间。但对可用性要求极高,甚至六个九,因为配置可以获取慢,但不能获取不到!

以上是关于大厂都是如何对高并发系统做到高可用的?的主要内容,如果未能解决你的问题,请参考以下文章

对高并发的理解(转载)

对高并发的理解(转载)

HttpClient对高并发有啥优化吗

对高可用系统的一点理解

高并发系统设计:系统设计目标②系统怎样做到高可用?

浅谈长连接keepalive和套接字重用reuse对高并发的影响