「这是我参与11月更文挑战的第10天,活动详情查看:2021最后一次更文挑战」。
前言
本文已收录到 Github-java3c ,里面有我的系列文章,欢迎大家Star。
Hello 大家好,我是l拉不拉米
,在我的『超级架构师』专栏前一篇文章中『超级架构师』服务限流的思路与手段,讲解了服务限流的手段和思路,今天给大家继续讲解服务降级的思路和手段。
什么是服务降级
当服务器压力剧增的情况下,根据实际业务情况及流量,对一些服务和页面有策略的不处理或换种简单的方式处理,从而释放服务器资源以保证核心交易正常运作或高效运作。
举个例子:某电商平台在双11的时候,流量、订单量、支付量暴增,为了保证最核心的订单服务和支付服务的稳定可用,主动将发送短信、评价等非核心业务功能切掉,让更多的CPU、IO资源给到核心服务。
使用场景
当整个微服务架构整体的负载超出了预设的上限阈值或即将到来的流量预计将会超过预设的阈值时,为了保证重要或基本的服务能正常运行,我们可以将一些 不重要 或 不紧急 的服务或任务进行服务的 延迟使用 或 暂停使用。
服务等级定义
服务等级定义 SLA(Service Level Agreement)
是判定压测是否异常的重要依据。压测过程中,通过监控核心服务状态的 SLA 指标数据,可以更直观地了解压测业务的状态。
SLA则是服务商与您达成的正常运行时间保证。
关于这个的详细解释,可以参考阿里云的介绍:服务等级定义SLA,这里不过多描述,SLA 分为网络服务和云服务,提供商的在线保证率通常要求达到6个9。
6个9含义
6个9指99.9999%,也就是一个服务有99.9999%概率是安全的,6个9有多安全呢?
2个9 = (1-99%)X24 X 365 = 87.6 小时 = 3.65天
3个9 = (1-99.9%)X24 X 365 = 8.76 小时
4个9 = (1-99.99%)X24 X 365 = 0.876 小时 = 52.56分钟
5个9 = (1-99.999%)X24 X 365 = 0.0876 小时 = 5.256分钟
6个9 = (1-99.9999%)X24 X 365 = 0.00876 小时 = 0.5256分钟 = 31秒
也就是,一年当中,6个9的安全性最多会有31s服务是不可用,相对来说是极高的。
分级评估模型
当微服务架构发生不同程度的情况时,我们可以根据服务的对比而进行选择式舍弃(即丢车保帅的原则),从而进一步保障核心的服务的正常运作。
如果等线上服务即将发生故障时,才去逐个选择哪些服务该降级、哪些服务不能降级,然而线上有成百上千个服务,则肯定是来不及降级就会被拖垮。同时,在大促或秒杀等活动前才去梳理,也是会有不少的工作量,因此建议在开发期就需要架构师或核心开发人员来提前梳理好,是否能降级的初始评估值,即是否能降级的默认值。
我们利用数学建模的方式或架构师直接拍脑袋的方式,结合服务能否降级的优先原则,并根据台风预警(都属于风暴预警)的等级进行参考设计,可将微服务架构的所有服务进行故障风暴等级划分为以下四种:
评估模型:
- 蓝色风暴 —— 表示需要小规模降级非核心服务
- 黄色风暴 —— 表示需要中等规模降级非核心服务
- 橙色风暴 —— 表示需要大规模降级非核心服务
- 红色风暴 —— 表示必须降级所有非核心服务
设计说明:
- 故障严重程度为:蓝色<黄色<橙色<红色
- 建议根据二八原则可以将服务划分为:80%的非核心服务+20%的核心服务
以上模型只是整体微服务架构的服务降级评估模型,具体大促或秒杀活动时,建议以具体主题为中心进行建立(不同主题的活动,因其依赖的服务不同,而使用不同的进行降级更为合理)。当然模型可以使用同一个,但其数据需要有所差异。最好能建立一套模型库,然后实施时只需要输入相关服务即可输出最终降级方案,即输出本次大促或秒杀时,当发生蓝色风暴时需要降级的服务清单、当发生黄色风暴时需要降级的服务清单。
降级权值
微服务架构中有服务权值的概念,主要用于负载时的权重选择,同样服务降级权值也是类似,主要用于服务降级选择时的细粒度优先级抉择。所有的服务直接使用以上简单的四级划分方式进行统一处理,显然粒度太粗,或者说出于同一级的多个服务需要降级时的 降级顺序 该如何?甚至我想要人工智能化的 自动降级,又该如何更细粒度的控制?
基于上述的这些AI化的需求,我们可以为每一个服务分配一个降级权值,从而便于更加智能化的实现服务治理。而其评估的数值,同样也可以使用数学模型的方式进行 定性 与 定量 的评估出来,也可以架构师根据经验直接拍脑袋来确定。
核心设计
降级处理
兜底数据
这方面有很多例子,比如某些页面挂了会返回寻亲子网。可以对一些关键数据设置一些兜底数据,例如设置默认值、静态值、设置缓存等。
- 默认值: 设置安全的默认值,不会引起数据问题的值,比如库存为0。
- 静态值:请求的页面或api无法返回数据,提供一套静态数据展示,比如加载失败提示重试,或者寻亲子网,或者跳到默认菜单,给用户一个稍微好一点的体验。
- 缓存: 缓存无法更新便使用旧的缓存。
限流降级
限流顾名思义,提前对各个类型的请求设置最高的QPS阈值
,若高于设置的阈值则对该请求直接返回,不再调用后续资源,也就是当流量洪峰到达的时候,可能需要丢弃一部分用户来保证服务可用性,对于丢弃的用户可以提供友好的提示,比如提示用户当前繁忙、稍后重试等。
限流需要结合压测
等,了解系统的最高水位
,也是在实际开发中应用最多的一种稳定性保障手段。当服务器压力剧增的情况下,根据当前业务情况及流量对一些服务和页面有策略的降级,以此释放服务器资源以保证核心任务的正常运行。
超时降级
对调用的数据设置超时时间
,当调用失败时,对服务降级。
举个例子,当访问数据已经超时了,且这个业务不是核心业务,可以在超时之后进行降级,比如商品详情页上有推荐内容或者评价,但是可以降级显示评价暂时不显示,这对主要的用户功能——购物,不产生影响,如果是远程调用,则可以商量一个双方都可以接受的最大响应时间,超时则自动降级。
故障降级
如果远程调用的服务器挂了(网络故障、DNS故障、HTTP服务返回错误),则可以进行降级,例如返回默认值或者兜底数据或者静态页面,也可以返回之前的缓存数据。
重试/自动处理
客户端高可用:提供多个可调用的服务地址。
微服务重试:dubbo重试机制。
API调用重试:当达到重试次数后,增加访问标记,服务降级,异步探测服务是否恢复。
WEB端:在服务不可用时,web端增加重试按钮或自动重试可以提供更友好的体验。
自动重试需设置重试次数和数据幂等处理。
降级开关
在服务器提供支持期间,如果监控到线上一些服务存在问题,这个时候需要暂时将这些服务去掉,有时候通过服务调用一些服务,但是服务依赖的数据库可能存在,网卡被打满了,数据库挂了,很多慢查询等等,此时要做的就是暂停相关的系统服务,也就是人工使用开关降级。开关可以放在某地,定期同步开关数据,通过判断开关值来决定是否做出降级。
开关降级还有一个作用,例如新的服务版本刚开发处在灰度测试阶段,不太确定里面的逻辑等等是否正确,如果有问题应该可以根据开关的值切回旧的版本。
在服务调用方设置一个flag,标记服务是否可用,另外key可以存储存储在在本地,也可以存储在第三方的配置文件中,例如数据库、redis、zookeeper中。
爬虫和机器人
分析机器人行为:短时间连续操作,agent,行为轨迹、拖拽(模拟登陆/秒杀/灌水)。
爬虫:引到到静态页或缓存页。
读降级
简而言之,在一个请求内,多级缓存架构下,后端缓存或db不可用,可以使用前端缓存或兜底数据让用户体验好一点。
对于读服务降级一般采用的策略有:
- 暂时切换读:降级到读缓存、降级到走静态化
- 暂时屏蔽读:屏蔽读入口、屏蔽某个读服务
通常读的流程为: 接入层缓存→应用层本地缓存→分布式缓存→RPC服务/DB
我们会在接入层、应用层设置开关,当分布式缓存、RPC服务/DB有问题时自动降级为不调用。当然这种情况适用于对读一致性要求不高的场景。
页面降级、页面片段降级、页面异步请求降级都是读服务降级,目的是丢卒保帅,保护核心线程,或者因数据问题暂时屏蔽。
还有一种是页面静态化场景:
- 动态化降级为静态化:比如,平时网站可以走动态化渲染商品详情页,但是,到了大促来临之际可以将其切换为静态化来减少对核心资源的占用,而且可以提升性能。其他还有如列表页、首页、频道页都可以这么处理。可以通过一个程序定期推送静态页到缓存或者生成到磁盘,出问题时直接切过去。
- 静态化降级为动态化:比如,当使用静态化来实现商品详情页架构时,平时使用静态化来提供服务,但是,因为特殊原因静态化页面有问题了,需要暂时切换回动态化来保证服务正确性。以上都保证了出问题时有预案,用户可以继续使用网站,不影响用户购物体验。
写降级
大家都知道硬盘性能比不上内存性能,如果访问量很高的话,数据库频繁读写可能撑不住,那么怎么办呢,可以让内存(假如是Redis)库来暂时满足写任务,同时将执行的指令记录下来,然后将这个信息发送到数据库,也就是不在追求内存与数据库数据的强一致性,只要数据库数据与Redis数据库中的信息满足最终话一致性即可。
也就是说,正常情况下可以同步扣减库存,在性能扛不住时,降级为异步。另外,如果是秒杀场景可以直接降级为异步,从而保护系统。还有,如下单操作可以在大促时暂时降级,将下单数据写入Redis,然后等峰值过去了再同步回DB,当然也有更好的解决方案,但是更复杂,不是本篇的重点。
还有如用户评价,如果评价量太大,那么也可以把评价从同步写降级为异步写。当然也可以对评价按钮进行按比例开放(比如,一些人看不到评价操作按钮)。比如,评价成功后会发一些奖励,在必要的时候降级同步到异步。
在CAP理论
和BASE理论
中写操作存在于数据一致性这个环节,降级的目的是为了提供高可用性,在多数的互联网架构中,可用性是大于数据一致性的。所以丧失写入数据同步,通过上面的理论,我们也能勉强接受数据最终一致性。高并发场景下,写入操作无法及时到达或抗压,可以异步消费数据、cache更新、记log日志等方式。
前端降级
当系统出现问题的时候,尽量将请求隔离在离用户最近的位置,避免无效链路访问, 在后端服务部分或完全不可用的时候,可以使用本地缓存或兜底数据,在一些特殊场景下,对数据一致性要求不高的时候,比如秒杀、抽奖等可以做假数据。
JS降级
在js中埋降级开关,在访问不到达,系统阈值的时候可以避免发送请求
主要控制页面功能的降级,在页面中,通过JS脚本部署功能降级开关,在适当时机开启/关闭开关。
接入层降级
可以在接入层,在用户请求还没到达服务的时候,通过Nginx + Lua、Haproxy + lua过滤无效请求达到服务降级的目的, 主要控制请求入口的降级,请求进入后,会首先进入接入层,在接入层可以配置功能降级开关,可以根据实际情况进行自动/人工降级。这个可以参考第17章,尤其在后端应用服务出问题时,通过接入层降级从而给应用服务有足够的时间恢复服务。
应用层降级
主要控制业务的降级,在应用中配置相应的功能开关,根据实际业务情况进行自动/人工降级。
SpringCloud中可以通过Hystrix配置中心可以进行人工降级,也可以根据服务的超时时间进行自动降级, Hystrix是Netflix开源的一款针对分布式系统的延迟和容错库,目的是用来隔离分布式服务故障。它提供线程和信号量隔离,以减少不同服务之间资源竞争带来的相互影响;官网讲Hystrix提供优雅降级机制;提供熔断机制使得服务可以快速失败,而不是一直阻塞等待服务响应,并能从中快速恢复。Hystrix通过这些机制来阻止级联失败并保证系统弹性、可用。下图是一个典型的分布式服务实现。
片段降级
例如打开淘宝首页,这一瞬间需要加载很多数据,有静态的例如图片、CSS、JS等,也有很多其他商品等等,这么多数据中,如果一部分没有请求到,那么就可以片段降级,意思是就不加载这些数据了,用其他数据顶替,例如其他商品信息或者等等。
提前预埋
这个很容易理解,大家应该都记得,每次双十一之前,淘宝总会提醒你下载更新,按道理来讲,活动还没开始,更新啥呢?
做法是对于一部分静态数据可以提前更新到你手机上,当你双十一时就不用再远程连接服务器加载了,避免了消耗网络资源。
总结
从微服务架构全局的视角来看,我们通常有以下是几种常用的降级处理方案:
- 页面降级 —— 可视化界面禁用点击按钮、调整静态页面
- 延迟服务 —— 如定时任务延迟处理、消息入MQ后延迟处理
- 写降级 —— 直接禁止相关写操作的服务请求
- 读降级 —— 直接禁止相关度的服务请求
- 缓存降级 —— 使用缓存方式来降级部分读频繁的服务接口
针对后端代码层面的降级处理策略,则我们通常使用以下几种处理措施进行降级处理:
- 抛异常
- 返回NULL
- 调用Mock数据
- 调用Fallback处理逻辑
最后
创作不易,感谢您的点赞!!🙏🙏
本文转载自: 掘金