基于系统稳定性建设,你做了哪些事情?(下)

上期聊完了如何提升系统可靠性,我们继续聊,如何提升系统可用性及稳定性。

2、提升系统可用性,缩短故障时间,快速止损

故障时长 = 发现问题时长 + 定位问题时长 + 解决问题时长

图片


上线规范:

上一章在“可靠性”的章节,也提到了上线规范,但出发点不同,基于“可用性”的上线规范,主要是从“快速止损”的角度,即:发现、定位、解决闭环来思考的。

  • 观察巡检。 在灰度上线以后,没有发现问题,我们继续推全量后,这个时候,我们需要从三个维度进行持续地观察巡检。

    ① 基础维度,我们需要观察CPU、内存、IO、网络、JVM等,上线前后的各项指标是否有明显异常。

    ② 应用维度,我们需要观察服务接口的QPS和响应时间(包括平均响应时间、95Line、尤其是99Line),上线前后是否存在明显异常。

    ③ 业务维度,这个相对简单一些,系统里面的核心流程走一遍,观察一下业务日志有没有报异常,就可以了。

    为什么我们灰度上线完,在全量上线后还需要观察巡检得如此详细,这是因为灰度上线,如果是用户维度灰度的话,样本率过低,机器维度灰度的话,缓存和DB等共用资源层面压力给得不够,所以并不能完全排除问题。

  • 回滚方案。 如果在观察巡检中发现了问题,那么我们下一步要做的就是,如何去解决问题。但是,如果我们没有针对于本次上线事先做回滚方案,而是临时见招拆招的话,会极大限度地延长了故障时间。一般来讲,常见的回滚方案有:代码回滚、表结构回滚、数据回滚,以及程序开关切换等。


监控告警:

解决的是三部曲中,发现问题时长方面。

监控也是分为上面说的三个维度,即基础维度、应用维度和业务维度,需要日常进行巡检来保证范围的足够覆盖,当发生问题的时候,一定是先收到我们系统自己的监控报警,而不是让用户和业务人员先反馈过来。

报警也是有一定策略的, 强调的是exactly once(精确一次),这本身也是一种做减法的过程。因为过多的报警(误报),不仅对工程师是一种打扰,且长期处于“狼来了”的情况下,反而会让工程师对报警变得忽视,出现处理不及时或者不处理的情况。


应急预案:

故障三部曲中,定位问题和解决问题方面。

在发生故障的时候,大多数人的脑子都是一片空白,很难迅速做出最合理的反应。衡量一个应急预案的好坏,关键是看它是否足够“无脑化”。

举个例子:如果发现了故障A,那么我们需要排查B和C两个方面来精准定位问题,定位后,我们再通过D—>E—>F三个操作来解决问题。

即:整个过程中,只需要照做,不需要思考,因为思考就会产生选择,而选择取舍是最耗费时间的事情,如果做不到这点,那么证明应急预案还有优化的空间。

另外,预案也不是一蹴而就的事情,需要跟随架构和业务的演进,不断进行更新迭代,同时,也需要不断做减法,该摒弃的摒弃,该合并的合并,如果只增不减,那么一年以后,预案就会变成一本书。


故障演练:

故障三部曲中,定位问题和解决问题方面。

故障演练需要从已知、半已知和未知三个维度去做。

  • 已知:已知故障类型,按照应急预案SOP,一步一步去做,从生疏到熟练,从20分钟到5分钟,从有限的人能做变成所有人能做。

  • 半已知:在已知故障类型的演练中,发现了未知因素,如:当缓存集群故障的时候,切换到备用缓存集群,但发现备用缓存集群里面的缓存数据有问题,需要重新清空及重新进行缓存预热。

  • 未知:未知故障类型,临时决策安排人员有序散开排查,临时根据现象定位问题,采用回滚版本或者重启等方式尝试性解决问题,或定位到问题后,采用有损的方式试图临时解决问题等。


自动防御:

这是件很能体现出工程师能力和素养的事情,需要工程师在coding过程中,在任何关键环节都具备安全意识。如:

  • 当下游依赖的存储集群,由于不可用而触发代码中的失败次数或失败时间阈值时,自动切换到备用的存储集群。
  • 当下游依赖的核心服务,由于不可用而触发代码中的失败次数或失败时间阈值时,自动降级到备用方案,如:将请求切换到存储非实时数据的ES中,接受有损。
  • 当系统由于响应时间激增而导致服务不可用时,自动对下游依赖的非核心服务进行降级熔断,减少服务接口的整体响应时间,保证可用。

3、提升系统稳定性,在可用、可靠的前提下,性能稳定

如果说,提升系统可靠性和可用性是重要紧急的事情,那么保持性能稳定就属于重要,但又不那么紧急,却需要长期坚持的事情。属于那种“身是菩提树,心是明镜台。时时勤拂拭,以免惹尘埃。”

可用性和可靠性更加关注服务接口的长尾耗时(响应时间的99Line),因为有可能少量慢请求会拖挂整个服务。那么性能稳定则更加关注服务接口的平均响应时间,且这个需要以周环比和日环比的方式来进行量化关注的,如果系统的平均响应时间在逐渐慢慢变长,那么就需要引起高度重视了。

  • 如果服务是因为接口的数量逐渐增加,从而导致系统整体负载慢慢变高,那么就需要考虑以业务模型维度进行服务拆分了,因为系统架构没有一蹴而就的,都是根据业务情况来逐渐演化,同时,最好连DB拆分一起做了,这样也可以降低DB的负载。

  • 如果服务的接口数量并没怎么增加,而是由于数据库主表的数据量越来越多从而变慢了,那么就可以考虑进行水平分库分表、或者冷数据归档来降低系统压力了,因为这个时候,所有的瓶颈可能都在这张主表上,服务拆分所带来的收益并不大。

  • 如果服务的接口数量没有增加,但是几个主要业务接口的业务复杂度增加了,比如:以前这个接口只需要2次DB查询和3次下游服务调用,现在发展成10次DB查询,12次下游调用了,且并行调用、缓存、SQL优化等各种优化方式已经用到极致了,这个时候,我们就需要进行接口拆分+服务拆分了。先按照业务维度和重要等级维度,把一个大接口拆分成几个小接口,同时按照业务模型和重要等级的角度,把服务进行拆分,给以后的业务继续扩展留下空间。

全文完。