面试官:如何构建一个高可用的系统?(3)

前两章《面试官:如何构建一个高可用的系统?》、《面试官:如何构建一个高可用的系统?(2)》主要是从"减少故障次数" 和 "降低故障时长"的角度进行分析的。

本文我们从"缩小故障范围"的角度,再来详细讲讲。

缩小故障范围的核心点在于四个字------"简化链路"。

对于分布式系统来说,通过一些手段进行"简化链路",这样可以把整个系统从全线崩盘的大故障,变成局部可控的小故障,以保证系统的可用性,降低公司的业务损失。

熔断

熔断机制,是指在分布式系统中,当某个下游服务出现超时、错误率过高或资源不足等过载现象时,上游服务会迅速切断对该下游服务的请求,以避免出现故障扩散的情况。

熔断机制可以保证整个系统的可用性,避免因一个服务的局部小规模故障,导致整个系统全局瘫痪的后果。

上述概念有些枯燥晦涩,我们以上图进行举例说明:

电商的商品详情页接口,用户端服务需要调用商品中心、营销中心、门店服务、推荐服务和评价服务,来完成商品详情页的数据聚合展示。

正常情况下,我们假定商品详情接口需要200ms,其中用户端服务本身需要消耗50ms,其余的五个下游服务各消耗30ms。

那么单线程每秒可以处理5个接口请求,以Tomcat默认的最大线程数(maxThreads)等于200来算,用户端单服务器最大可承载1000 QPS。

公式如下:

1s / (50ms + (30ms * 5)) * 200 = 1000 QPS

但有一天评价服务出现异常,原本的30ms的耗时变成了830ms,那么公式如下:

1s / (50ms + (30ms * 4)+ 830ms) * 200 = 200 QPS

在系统吞吐量降为原来的1/5的情况下,非常容易被用户的高并发请求打挂。

此时,我们就需要程序中的自动熔断策略,在服务有损的情况下,将商品详情页接口耗时依然维持在原来的200ms左右。

现在主流的实现熔断功能的断路器组件,是Netflix Hystrix和Alibaba Sentinel,两者对比如下:

至于代码级的使用细节和参数解释,由于篇幅的原因,这里先不过多解释了。

唯一要提醒的是,只有下游依赖服务中的弱依赖才可以进行熔断,强依赖熔断了就没有价值了,下面我再解释一下强弱依赖:

  • 强依赖:假定服务A依赖于服务B,服务B出现故障不可用时,服务A也不可用。
  • 弱依赖:假定服务A依赖于服务B,服务B出现故障不可用时,服务A虽然受到了些许影响,但是仍然可用。

降级

服务降级,是指当系统出现高负载或异常时,通过牺牲部分非核心功能的方式,保证系统核心功能的可用性,这是一种弃车保帅的策略。

我们依然用电商场景进行举例:

电商的商家端管理系统,商家每天需要登录系统进行商品、库存和订单的管理,以保证业务的正常运转,此为核心功能。

而对于评价、财务的管理,以及商家自促和统计分析功能,其重要性和实时性要求相对较低,此为商家后台的非核心功能。

那么当商家端管理系统出现高负载或异常时,我们对其进行降级操作,把上图右侧的非核心功能通过拒绝请求的方式,保证上图左侧的核心功能可用。

另外,基于此降级场景,最好的方式是通过配置中心以修改参数的方式操作降级,而不是现场修改业务代码进行Hotfix的方式。

此外,我们还可以对功能繁多的复杂系统采取自定义多Level降级的方式,在保证系统可用性的同时,尽量兼顾用户体验。

我们还以上图的商家端管理系统举例,配置中心的表达式如下:

  • degradation.level = 0(默认值,不启用降级)
  • degradation.level = 1(启用轻度降级,拒绝"统计分析"功能)
  • degradation.level = 2(启用中度降级,拒绝"统计分析"和"商家自促"功能)
  • degradation.level = 3(启用重度降级,拒绝上图右侧的所有非核心功能)

当然,此方案适合于功能繁多的复杂系统,如果是上图的商家端管理系统,其实一个功能对应一个降级配置是最灵活的。

链路拆分

分布式系统中,各个微服务是按照其重要程度分等级的。

我们以电商场景进行举例:

P0级服务:当该服务不可用时,会导致用户侧业务完全瘫痪、不可进行。如:用户端聚合服务、商家端聚合服务、商品中心、订单中心、用户中心、支付服务。

P1级服务:当该服务不可用时,用户侧业务虽然可以继续运行,但会收到较大影响;或是公司内部业务不可进行。如:营销平台、评价服务、CRM系统、上单系统。

P2级服务:当该服务不可用时,用户侧业务轻微影响体验,或是公司内部业务虽然可以继续进行,但会受到较大影响。如:消息中心、图像检索服务。

P3级服务:重要程度不高的内部系统,如:OA系统、报表系统、自动化测试平台等。

链路拆分的全称是"核心链路拆分",即:识别并拆分出P0级别的服务,使其与非P0级服务在资源上(MySQL、Redis、MQ、ES等)进行完全隔离,在服务间可具备可隔离性(熔断、降级、代码级容错处理),以保证系统核心功能的可用性。

链路拆分和降级所要达成的目标是一致的,只是要复杂很多,涉及到了系统的整体架构设计。

请见上图的电商服务,当然,真正意义上的电商业务,服务数量要远比这多很多,这里仅作为举例。

(1)图左侧的服务均为核心链路的服务(P0级服务),意思是只要这些服务不挂,电商业务虽然会影响些许体验,但业务依然会正常运行。

(2)图左侧的中间件资源(Redis、MySQL、Redis)也都是作为核心链路独立的,意味着其不会被图右侧的非核心服务的读写操作所影响。

(3)图左侧的服务会依赖图右侧的服务,比如:用户端服务会依赖评价系统服务,如果评价系统服务慢了或者挂了,只要做好熔断降级或代码级容错处理,业务依然会正常运行。

(4)图右侧的非核心服务,尽量不要依赖于图左侧的核心服务,如果有业务诉求的话,可以独立部署一套与核心服务代码完全相同的非核心服务以供使用。如:图右侧非核心链路中的商品中心、订单中心和用户服务。

(5)图左侧核心链路的服务数量是要控制的,因为数量越多,链路环节就越长,不可控的未知因素就越多。如果核心链路的服务数量超过全部服务的15%,那就需要进行服务梳理整合。

另外,链路拆分是一项极其耗时且复杂的工作,研发团队在考虑此策略时,一定要衡量好ROI(投入产出比)。

个人建议,如果是一天都没多少业务量的创新业务或边缘业务,那就免了吧。

切MVP版本

MVP,最小可行产品(Minimum Viable Product),该策略往往是在上述三种策略都失效后才启用的,是"缩小故障范围"中的终极保命大招,但对用户体验的伤害也是最大的。

如果说,正常产品形态是美味可口的满汉全席的话,那MVP版本只是能勉强填饱肚子的窝头咸菜。

上述概念有些枯燥晦涩,我们以上图进行举例说明:

某在线教育平台,正常产品形态具备上图右侧的N多功能,但当系统发生故障,用了"熔断"、"降级"、"链路拆分"策略依然没能解决问题时,只能把系统切到图左侧只具备"登录"、"查看课表"和"上课"的MVP版本中,保证学生在较差的用户体验下,可以正常上课。

一般来讲,MVP版本是用一个单体服务的方式,来完成系统仅存的所有功能的,且该单体服务与图右侧的整个系统服务 + 中间件服务,是完全分开独立部署的,只为完全简化和解耦化。

结语

整个关于系统可用性的三部曲就全部讲完了,因为涉及的知识点太多,所以没能讲到代码级的细节中,后续有机会再进行深入讲解。

相关推荐
AI人工智能+电脑小能手2 小时前
【大白话说Java面试题 第87题】【Mysql篇】第17题:分布式事务的实现原理?
java·数据库·分布式·mysql·面试
红尘散仙2 小时前
我把终端小说阅读器接上了 AI Agent:TRNovel 现在能用 skill 生成书源了
人工智能·后端·rust
卷毛的技术笔记4 小时前
告别硬编码!Spring AI Alibaba 实现 AI Agent 智能工具调用(Tool Calling)
java·人工智能·后端·python·spring·ai编程
_codemonster4 小时前
30分钟快速搭建 Spring Cloud Alibaba 微服务实战(一)
微服务·架构·毕业设计·课程设计
会编程的土豆4 小时前
Go 语言反射(Reflection)详解
开发语言·后端·golang
Cosolar4 小时前
从零写一个 Attention Is All You Need
人工智能·面试·架构
喵个咪4 小时前
GoWind Toolkit Go后端代码生成 完整全流程实战
后端·go·orm
basketball6165 小时前
Go 语言从入门到进阶:4. 数组和MAP使用方法总结
开发语言·后端·golang
qq_2518364575 小时前
SpringBoot+Vue 共享电池柜管理系统 完整实现 前后端分离项目实战 完整代码
vue.js·spring boot·后端
zhangxingchao5 小时前
AI 大模型核心六:量化、Workflow 与 Agent、多轮 RAG
前端·人工智能·后端