可靠性设计工作应遵循以下几个原则。
□应将产品的可靠性要求转化为可考核验证的设计要求,作为可靠性设计的依据。
□应分析业务的请求链路面临的负载量、运行环境,确定对系统承载能力的要求。
□应该对性能、可靠性、成本、技术条件等综合权衡。
□应该研究公司内部或业界的类似产品,了解其故障模式、薄弱环节和影响因素。
□识别所有环节中存在的单点,

对关键链路上的组件应该进行冗余设计。
□控制在一次迭代中引入新组件、新模块的数量。
□对可能过载的模式进行降级/限流/熔断设计。
□对系统的关键可靠性指标设计各类监控能力。
□对系统自身可靠性不足又难以解决的问题制定修复方案。
□尽早进行可靠性设计,在早期把稳定性/可靠性做好是成本最低的。
□可靠性目标应循序渐进地提高,不在早期追求过高目标。
□要认识到架构是动态演进的,在不同的业务发展阶段、不同的规模阶段设计不同的架构。

靠性领域的4种通用设计方法在互联网架构设计中也同样适用,总结起来包括避错设计(预防错误)、查错设计(定界定位诊断感知)、容错设计(容灾/自愈)、纠错设计(修复错误)4个方面的方法。接下来分别讲述具体的方法。

1.避错设计
避错设计是在软件架构、开发和运维过程中,针对具体软件特征,应用有效的软件工程、架构技术、运维技术、方法、工具,加强管理,避免引入软件错误,在架构上支持避错设计,保证软件可靠运行。避错设计与常规的软件设计融为一体、相互支持、互相补充。架构师与设计师共同把关,尽量避免错误和脆弱因素,遵循业界的最佳实践,具体可以归结为以下几条。

(1)简化设计
复杂是可靠性的天敌,简化设计可以避免很多错误。简化设计方法的具体内容如下。
□控制程序复杂度,尽量采用微服务架构使功能模块低耦合、高内聚,用服务治理方式进行自动化调用管理。
□可以综合参考模块的扇入扇出的数量进行模块拆分,减少模块间调

用的复杂结构,使模块的规模保持适中,将故障产生的影响控制在可接受范围内。
□合理利用中间件,在需要的时候使用中间件来降低直接耦合,比如生产者消费者模式。在系统层尽量使用统一基础设施,不使用过多的不同类组件。
□在业务功能上,把大块功能拆分成独立的模块,独立部署、运维。模块化设计的分解方法有多种,比如按功能分解、按数据分解。
□评估接口的复杂度和冗余度,提供一致性、幂等性等。复杂度如果管理不善很容易失控,所以简化设计、加强可控性是解决可靠性问题的方法。

(2)健壮性设计
健壮性主要是指各种已知或未知的故障因素出现时系统还能自我保护的能力。系统应尽量做到以下两点。
□已知问题预先处理。对可能发生的错误进行预处理,避免错误发生,或设计补偿措施,如单个节点失

败后重试其他冗余节点。做好超时处理,要设置访问其他接口的超时时间,也要考虑极端情况,如在高峰期所有请求都超时时如何处理。
□互不信任。不信任上游的输入和调用,对上游调用方发来的请求做好合法性检查、异常处理和保护;不信任下游被调用方,对调用的接口从强依赖变成弱依赖并做到可降级。

(3)隔离设计
隔离设计是为了在故障发生时控制影响范围,特别是实现故障与核心服务的隔离。常见的隔离技术包括主机隔离、线程池隔离、进程隔离、集群隔离、用户隔离、逻辑隔离、数据隔离等。可以通过故障隔离措施隔离故障节点,抑制故障蔓延和传播,降低损失。2.查错设计
通过避错设计能减少软件故障,但是当软件越来越复杂且快速迭代更新时,我们很难完全避免错误,所以还应设计系统具备高效的查错能力。广义地讲,代码审查(CodeReview)、测试、监控都属于查错方法。查错设计包括外部查错设计和内部查错设计。外部查错是指从外部进行拨测/探测从而发现异常,探测的状态可持续上报。内部查错是软件系统自身具备查错的功能,在软件错误出现的时候捕获并感知错误。(1)外部查错设计
外部查错是通过外部检测程序主动对系统进行检测、监控。例如:定期扫描拨测服务的接口,通过状态码确认错误;通过监控队列长度,检测系统目前处理能力;通过主动检测上报的错误数据来分析、定位错误状态、时长、严重程度等。故障检测是指通过外部监控或监控系统检测服务出现的异常或故障。
定位能力是衡量主动查错的指标体现,如在故障出现后用多长时间发现故障,多长时间查到故障,多长时间定位到根因。查错技术设计包括的范围非常广,不仅包括如产品自身的健康检测、数据收集、主动上报、拨测等功能的设计,还包括产品之外的日志体系、全链路监控、APM、Tracing追踪、Profiling技术、外部拨测、异常检测/根因定位、全景监控、立体诊断等功能的设计。更多内容将在第4章展开讲解。(2)内部查错设计
被动式查错是当错误发生时,由对应的检测机制来捕获并感知到错误。内部查错可以在关键环节建立检测机制,内部检测措施也为运维监控提供了有效手段。捕获到错误后可打印错误日志,再通过日志监控告警及时发现问题。上下游程序应实现自我保护,不相信其他模块的输入,必须验证输入是否合法,在发现不合法输入后要立即检测、纠正、返回报错。内部查错中的指标监测结果还经常是后面容错、纠错的决策依据。
曾有这样一个真实故障案例,某后台需要下发一个广告位置信息到端上,位置信息是不允许为负数的,但在处理过程中系统并没有对位置信息进行负数判断,测试环节、端上逻辑也没有对负数进行处理,导致移动端

大面积崩溃。在这个例子中,如果任何一个环节有对负数的检测机制,就不会导致这个故障。这是典型的盲目信任上游输入造成的故障。3.容错设计
容错(Fault Tolerance)是指在局部故障发生后软件系统不对用户暴露错误,而是通过技术方法容忍或隔离故障,使得系统仍然能够工作或降低不良影响。完全或部分消除软件错误的影响,是容错的基本目标。容错和避错不同,容错是向系统提供保护措施,使得即使错误发生也不至于导致系统崩溃或失败,而避错是让错误不发生。容错技术分为结构冗余、信息冗余、时间冗余、降级容错等。(1)结构冗余
结构冗余包括硬件冗余、软件冗余和混合冗余。容错容灾首先要做的是在结构上消除单点。互联网架构中常见的多副本、多机容灾、机房容灾、异地容灾、异地多活、跨可用区架构等都是消除单点的设计。(2)信息冗余

信息冗余是通过缓存技术、副本技术、校验码技术等来实现的。比如通过从库只读、缓存数据、CDN技术等实现冗余容灾及性能的收益,通过RAID、校验码等技术实现文件存储的可靠性与成本的均衡。(3)时间冗余
时间冗余是指通过重复多次相同的计算来实现的,如通过异步处理、离线处理、幂等操作、定期对账技术等实现可靠性。

(4)降级容错
降级容错包括有损降级、限流、熔断等几种方式。
有损降级是把服务按重要程度分级,在故障发生时对某些低优先级或消耗资源较多的服务提供有损服务。比如暂时关闭评论系统、停用推荐服务,或暂时停止一些离线任务。这些非核心功能不影响用户的核心功能,在资源不足时能腾出资源给重要的服务使用。
限流容错是一种保护机制,它为了保护系统整体可用性,基于容量上限,限制超过容量的请求调用数量或限制某些不合理请求,从而保证部分用户是可用的。
服务熔断是指主调方根据被调方返回的错误数、耗时等指标来判断负载状态,在超载时自动开启保护措施的容错方法。
容错设计除了冗余设计和容错技术,还需要相关的配套措施。我们将在3.5节进行更为详细的讨论。4.纠错设计
纠错设计是指在服务运行中发生异常时软件能够自动纠错,有改进措施或补偿措施,在互联网业界常被称为“自愈”。如产品在发生故障时,能够采用可继续工作的冗余设备、冗余节点来提供服务,数据损坏可以通过备份数据来恢复等。纠错的前提是能准确感知、检测到软件错误,且软件系统有能力自我修复、排除错误。常见的纠错方法列举如下。
□调度/切换。在单节点出现异常时可以调度到其他节点、集群、线路等冗余结构中,可在多种模式之间进

行切换等。
□弹性扩容。当检测到工作负载超过现有最大容量时可触发自动扩充资源、扩大容量来承载用户访问需求。
□重建/重启。在重大灾难发生后,比如集群完全损坏、数据丢失,应该能通过本地或异地重建的方式恢复业务。纠错设计是快速恢复业务的手段,很多互联网公司都有快速恢复平台,可以协助SRE进行快速回滚、调度、切换、降级等操作。应尽可能自动触发修复程序,如磁盘清理脚本、自动摘除节点、自动扩容等。自动重启有时也是一种快速恢复、保护数据的办法。
举个例子,为了更及时地屏蔽故障节点,主调方根据下游被调服务的异常情况来判断某个节点是否异常,如果异常可主动摘除该节点。具体策略是,当主调方调用某个被调服务出现调用连续超时,且调用全部集中在某个被调节点时,主调方可在调用列表中屏蔽该节点,让流量分发到正常的节点上去。