稳定性之面向失败设计【过载保护】

在互联网架构中,高可用架构设计很重要的一个抓手就是过载保护,所谓的过载保护,是指当系统负载超过该系统或服务的承载能力时,系统会自动采取保护措施,确保自身不被压垮,从而提供有损服务,如果系统不能进行自我保护,很可能会导致雪崩现象。

下面通过12306网站的案例,详细解释下过载保护的重要性,12306初期开始网络订票时,每次春运抢票,12306网站都会卡顿,瘫痪,网站打不开等各种情况,原因是流量突发剧增,系统未做过载保护,才出现以上的情况,那么后来它是如何改进的呢,改进措施如下:

  • 用户登录时添加图片验证,防止抢票软件自动登录
  • 当用户请求比较频繁的时候,系统出现提示“您的操作频率过快请稍后重试”
  • 当流量过大时,系统会提示“系统繁忙,请稍后重试”

当系统负载过高时,系统资源不够,不足以应对大量请求,即系统资源与访问量出现矛盾的时候,我们为了保证有限的资源能够正常服务,通常会采用三种措施来保护系统,分别是限流、降级和熔断。

限流

限流是通过对在单位时间内流量速率进行限制,保证请求流量在合理范围内,避免因为超出预期的大流量导致服务整体性能下降、响应缓慢或不可用。有点类似于,在帝都早上我们坐地铁一样,如果人多上不去,只能排队,等待下一趟地铁,系统也是如此。至于限流触发机制可以是提前预设阈值,负反馈或下游通知。

限流场景

  • 限制总并发数(比如数据库连接池、线程池、服务并发数、某个接口)
  • 限制瞬时并发数(如Nginx的limit_conn模块,用来限制瞬时并发连接数)
  • 限制时间窗口内的平均速率(如Guava的RateLimiter、Nginx的limit_req模块,用来限制每秒的平均速率)
  • 限制远程接口调用速率、限制MQ的消费速率等。

另外,还可以根据网络连接数、网络流量、CPU或内存负载等来限流。

限流阈值

可以通过压测的方式,获取系统能力的上限,一般情况下,压测是为了获得两个结果,如下:

第一是速率,表示单位时间内能够处理的请求数据量,例如xxx次请求/秒。

第二是并发数,表示同一时刻能够处理的最大并发数,例如xxx次并发。

值得考虑的是,还需要获取最大值、平均值、中位值,限流阈值是需要根据这些指标中得来的,另外一个是考虑的是被拒绝的流量该如何处理?是否直接丢弃?不能该作何处理,可参考:

  • 拒绝服务(友好提示用户)
  • 排队或等待(比如秒杀、评论、下单)重新发起
  • 延迟处理(可以将处理不过来的请求,先放到池子中,不处理,后端程序从池子中依次取出,匀速消费处理,常见的可以用队列模式来实现)
  • 特权处理(可以根据用户标签或者级别进行特权处理,例如VIP用户优先处理,访客用户延迟处理或者不处理)

限流模型

限流算法模型,基于时间模型、桶模型两种方式,分别是

第一是时间模型:固定时间窗口、滑动时间窗口

第二是桶模型:漏桶、令牌桶

业界比较常用的框架或者工具:比如 Redis 配合lua脚本使用、Sentinel、Guava中的Ratelimiter来实现控制速率

限流实践

限流实践是对上面几种限流模型的一些分析,包括应用场合,每一种模型都有独特的特点,只有对这几个模型有一定的了解之后,结合应用场合选择。

首先是固定时间窗口,粗暴的方式,一般来说,如非时间紧迫,不建议选择该方案,但是为了能够快速止损眼前的问题,也可以作为临时应急的方案。

其次是滑动时间窗口,相对友好些,该方案适用于对异常结果高容忍的场景,原因是相比两个窗口少了一个缓存区。

然后是漏桶,个人觉得该方案适合作为通用方案,漏桶算法不能够有效地使用网络资源,因为漏桶的漏出速率是固定的,所以即使网络中没有发生拥塞,漏桶算法也不能使某一个单独的数据流达到端口速率。但宽进演出的思路在系统保护的同时还留有一些余地,使得他的使用场景更广。

最后是令牌桶,当你需要尽可能的压榨程序的性能(此时桶的最大容量必然会大于等于程序的最大并发能力),并且所处的场景流量进入波动不是很大(不至于一瞬间取完令牌,压垮后端系统)。

降级

降级是将系统的所有功能服务进行一个分级,当系统出现问题,需要紧急降级时,可将不是那么重要的功能进行降级处理,停止服务,防止该问题,影响主流程的稳定性,另一方面降级是为了主流程提供更多资源,让有限的资源发挥更大的价值,从而将非核心的功能进行降级,停止服务,这样可以释放出更多的资源供给核心功能的去用。

例如某电商平台中,在双十一促销活动中,可临时将商品评论、积分、退款等非核心功能进行降级,停止这些服务,释放出机器和CPU等资源来保障用户正常下单,而这些降级的功能服务可以等整个系统恢复正常后,再来启动,进行补单/补偿处理。除了功能降级以外,还可以采用不直接操作数据库,而全部读缓存、写缓存的方式作为临时降级方案。

可降级的点

在一个系统中可以降级的点,可以分为以牺牲用户体验、牺牲功能完整性、牺牲时效性这三大类,主流程或核心功能点是不能够降级的,所有的降级都是为主流程或核心功能点提供更多资源,或者为了保证主流程的稳定性。

牺牲用户体验。

第一以牺牲用户体验降级,来保证稳定性,例如:

  • 评价列表禁止10页之后的翻页
  • 使用通用内容代替个性化推荐内容
  • 或是提供降级友好界面提示用户,该功能因什么原因,预计什么时间恢复之类

第二以牺牲功能完整性,来保证稳定性,例如:

  • 适当关闭风控的行为,通过裸奔的形式,来节约资源
  • 适当关闭条件校验,如:积分商品添加到购物车时判断积分够不够

第三以牺牲时效性,来保证稳定性,例如:

  • 消息通知这类业务延迟处理
  • 优惠补贴这类业务延迟处理
  • 库存、售卖数量、这类调整静态文案展示

降级前置条件

降级前置条件是首先对我们的业务功能,进行分级,确定每个功能的「重要程度」,它决定了在什么情况下可以抛弃它以保证剩下的功能可用,有点类似于给日志定义级别一样,比如我们可以定义P0~5 五个级别,P0的级别最高,要拼死保护,P5的级别最低最先可以被降级掉。

对业务功能分级之后,针对不同级别的业务功能,进行上下游依赖分析,明确上下游依赖关系、SLA,降级方案设计包括降级开关、降级策略、降级之后上下游依赖容错处理机制。

降级策略

降级策略可以是人工、自动降级两种方式,自动降级实现起来相对比较复杂,例如平均响应时间、异常数、异常比例等多种方式,这种方式需要实时对降级指标计算与降级规则匹配,另外一种方式人工降级,相对容易,通过开关设计即可,当需要时打开开关,不需要时关闭开关即可。

降级原则

  • 明确降级成本收益比,选择收益最高的的方案
  • 降级方案需要定期Review,确保方案是最新的,随着业务变化
  • 程序所依赖的下游程序的级别不能低于该程序的级别
  • 弱依赖降级、事件解耦

降级案例分析

案例一、写服务降级、降级思路:同步写转异步写,例如扣减库存的操作,正常情况下的设计一般是:

方案1:数据库中扣减,成功后更新 Redis 缓存。

方案2:先扣减 Redis 缓存,同步扣减数据库,如果失败则回滚 Redis 缓存。

当数据库性能跟不上时,就需要采用异步方式了,先扣减 Redis 缓存,同时向队列中发送一条扣减数据库库存的消息,异步进行数据库扣减,实现最终一致性。

案例二、读服务降级、降级思路:缓存、静态化 ,从读取数据的角度考虑降级,读取缓存,例如商品详情页,其中有非常多的内容,比如商家信息、推荐信息、配送至信息、相关分类、热销榜等等,这些都不是核心数据,所以在出现异常时可以进行降级处理,还可将整个页面切换为静态化,最大程度的降级读服务。

案例三、默认数据、降级思路:默认值、或临近缓存数据、比如远程服务挂掉了,就自动降级,可以使用默认值、提前准备的内容、之前的缓存数据。

熔断

熔断机制这个词对你来说肯定不陌生,它的灵感来源于我们电闸上的“保险丝”,当电压有问题时(比如短路),自动跳闸,此时电路就会断开,我们的电器就会受到保护。不然,会导致电器被烧坏,如果人没在家或是人在熟睡中,还会导致火灾。所以,在电路世界通常都会有这样的自我保护装置。

在分布式系统设计中,熔断指的是在发起服务调用的时候,如果被调用方返回的错误率超过一定的阈值或触发某些特定策略,那么后续的请求将不会真正发起请求,而是在调用方直接返回错误或者兜底策略。一句话总结就是发现下游不能正常提供服务,不再对下游进行调用,而是主动直接返回错误或者兜底策略。

为什么要熔断

假定服务A依赖服务B,当服务B处于正常状态,整个调用是健康的,服务A可以得到服务B的正常响应。当服务B出现故障时,比如响应缓慢或者响应超时,如果服务A继续请求服务B,那么服务A的响应时间也会增加,进而导致服务A响应缓慢。如果服务A不进行熔断处理,服务B的故障会传导至服务A,最终导致服务A也不可用。

熔断三种状态

熔断器模式就是像是哪些容易导致错误操作的一种代理,代理能够记录最近调用发生错误的次出手,然后决定使用允许操作继续,或者立即返回错误,熔断器也能够诊断错误是否已经修正,如果已经修复,应用程序会再次尝试调用操作,因此熔断状态有关闭,打开,半开三种状态。

关闭状态通俗说是“识别系统不可用状态策略”,在这种情况下默认是关闭的,此时不做任何拦截,对全部流量进行放行,该状态下,计算并统计不正常状态的策略,一旦到达不可用状态,则进行打开状态,对所有的流量进行拦截,使用预案或者兜底机制。

半开状态通俗说是“尝试恢复正常”,在这种情况是一个中间状态,尝试从打开状态到关闭的状态的中间态,一般来说会放一些请求,具体多少流量是可以进行案例进行配置,尝试探测,在一定的时间窗口内,符合预设规则,如果满足可用状态,则状态设置成关闭状态,对全部流量进行放行。

打开状态通俗说是“识别系统是可用的状态策略”,在这种情况对所有流量进行拦截切断,影响和损失肯定还是有的,所以还是需要尽快恢复回来,以提供完整的服务能力,在这这种情况下,对部分流量流量进行下放,进行探测,故状态转移到半开状态。

熔断策略

熔断策略,是定义出可以判断服务可用或者不可用的指标,该指标判断形式,可以是阈值或者百分比,例如:

  • 在10秒内出现100次“无法连接”或者出现100次大于5秒的请求。
  • 在10秒内有30%请求“无法连接”或者30%的请求大于5秒。

常见的策略模式

  • 秒级 RT 模式:若持续进入 5 个请求,它们资源的平均响应时间都超过阈值(秒级平均 RT,以 ms 为单位),资源调用会被熔断。
  • 秒级异常比例模式:当资源的每秒异常数占通过量的比值超过阈值之后,资源进入降级状态,即在接下的降级时间窗口(在降级规则中配置,以 s 为单位))之内,对这个方法的调用都会自动地返回。
  • 分钟级异常数模式:当资源最近 1 分钟的异常数目超过阈值之后会进行熔断。

熔断策略,一般情况下还需要增加人为降级开关,便于紧急情况下使用,优先级要高于规则之上。

熔断原则

熔断很重要的一点是要区分对待,原因是一般服务都是集群模式,要确认单节点不可用还是整个服务集群都不可用。另外一点是熔断是大多数情况是被动,熔断往往是最后的选择,一旦熔断可能代表整个服务集群都不用,那么损失是可想而知,在架构设计中尽可能是选择限流或者降级的方案,因为“部分胜于无”,虽然无法提供完整的服务,但是还可以提供有损服务,尽可能往降低影响的方向去努力。

服务雪崩

服务雪崩是多个服务之间调用的时候,假设服务A调用服务B和服务C,服务B和服务C有调用其他的微服务,这就是所谓的”扇出”,如扇出的链路上某个微服务的调用响应式过长或者不可用,对服务A的调用就会占用越来越多的系统资源,进而引起系统雪崩,所谓的”雪崩效应”,熔断机制是应对雪崩效应的一种服务链路保护机制,

当扇出链路的某个服务不可用或者响应时间太长时,会进行服务的降级,进而熔断该节点微服务的调用,快速返回”错误”的响应信息。

限流、降级、熔断概述

不同点

  • 触发时机不同,服务熔断一般是下游某个服务不可用而引起的,而服务降级则一般是从整体负荷考虑。
  • 行为不同,熔断一般情况被动的,处于无奈的,降级一般是可主动或者被动,具备更多主动性。
  • 管理目标不同,熔断是每个微服务都需要的,是一个框架级的处理;而服务降级一般是关注业务,对业务进行考虑,抓住业务的层级,从而决定在哪一层上进行处理:比如在IO层,业务逻辑层,还是在外围进行处理。
  • 限流是对外部流量的行为,服务端根据其自身能力设置的一个过载保护,目标是对外。
  • 熔断是对内的流量的行为,是调用端对自身的一个降级保护,目标是对内。
  • 熔断和限流都可以认为是降级的一种方式。

共同点

  • 都是从可用性、可靠性出发,提高系统的容错能力。
  • 因不可控因素,使某一些应用不可达或不可用,来保证整体系统稳定。
  • 对其自治性要求很高。都要求具有较高的自动处理机制。

解决方案

业界流行的解决方案分别是Sentinel、Hystrix,两种解决方案各有各的优势,个人觉得Sentinel更优秀,功能丰富,有更完善的控制台,下面是两者区别:

#

Sentinel

Hystrix

隔离策略

信号量隔离

线程池隔离/信号量隔离

熔断降级策略

基于响应时间或失败比率

基于失败比率

实时指标实现

滑动窗口

滑动窗口(基于 RxJava)

规则配置

支持多种数据源

支持多种数据源

扩展性

多个扩展点

插件的形式

基于注解的支持

支持

支持

限流

基于 QPS,支持基于调用关系的限流

不支持

流量整形

支持慢启动、匀速器模式

不支持

系统负载保护

支持

不支持

控制台

开箱即用,可配置规则、查看秒级监控、机器发现等

不完善

常见框架的适配

Servlet、Spring Cloud、Dubbo、gRPC

Servlet、Spring Cloud Netflix

总结

总体来看,限流偏向于管理入口流量,熔断偏向于保护下游服务,降级是为了在系统触发负载保护时,尽可能提供弹性服务,一般来说,熔断比限流严重,而限流比降级严重,目标都是从可用性、可靠性出发,提高系统的容错能力和稳定性。

第十一篇:稳定性之面向失败设计【过载保护】相关推荐

  1. 第九篇:稳定性之面向失败设计【可用性架构设计、可用性容灾】

    前言 在互联网系统中,每个系统都有服务的上线,所以当流量超过服务极限能力时,系统可能会出现卡死.崩溃的情况,那么就需要各种手段或者策略,来保证系统的稳定性.可用性,系统以牺牲部分请求或延迟处理,来提供 ...

  2. 面向失败的设计之播控系统!

    作者 | 阿里文娱高级开发工程师 云琰浅 责编 | 屠敏 谈面向失败的设计 1.什么是面向失败的设计? 面向失败的设计,就是以"失败"为对象,天然为了失败而存在的设计思想,在一开始 ...

  3. 置之死地而后生,面向失败的架构设计

    导读:在架构设计方面,通过面向失败的设计,贯穿整个软件生命周期来防范已知的确定性风险及未知的不确定性风险. 搜一下"宕机",就能看到很多系统问题导致的服务不可用新闻,当然,这其中包 ...

  4. 奋斗的小孩系列 FPGA学习altera系列: FPGA学习altera 系列 第二十一篇 数码管设计

    奋斗的小孩系列 FPGA学习altera系列: FPGA学习altera 系列 第二十一篇 数码管设计 作者:奋斗的小孩 郝旭帅(转载请注明出处) 大侠好,欢迎来到FPGA技术江湖,江湖偌大,相见即是 ...

  5. C#WinCE程序(.NET Compact Framework 3.5)项目重构面向抽象设计

    重构关注点 遵循开闭原则 面向抽象设计 实现设备程序端可动态配置 重构的需求 领导想省事提出需求,将现有PDA程序修改为支持PC端由电器工程师根据实际的生产流程可配置,PDA程序在读取配置文件后动态生 ...

  6. 解剖SQLSERVER 第十一篇 对SQLSERVER的多个版本进行自动化测试(译)

    原文:解剖SQLSERVER 第十一篇 对SQLSERVER的多个版本进行自动化测试(译) 解剖SQLSERVER 第十一篇    对SQLSERVER的多个版本进行自动化测试(译) http://i ...

  7. Java异常处理终结篇——如何进行Java异常处理设计

    [本文转自于Java异常处理终结篇--如何进行Java异常处理设计] 有一句这样话:一个衡量Java设计师水平和开发团队纪律性的好方法就是读读他们应用程序里的异常处理代码. 本文主要讨论开发Java程 ...

  8. 关于领域驱动设计与面向数据库设计

    ╮(╯▽╰)╭ 开发时间越长越觉得自己的知识储备不够  本篇文章只代表虫子个人观点 欢迎大牛们吐槽 首先,虫子不会说哪一种更优秀,而是说对于新给的需求适合哪种设计.更贴切的一点,对于一个大型的系统应用 ...

  9. 秒杀多线程第十一篇 读者写者问题

    与上一篇<秒杀多线程第十篇 生产者消费者问题>的生产者消费者问题一样,读者写者也是一个非常著名的同步问题.读者写者问题描述非常简单,有一个写者很多读者,多个读者可以同时读文件,但写者在写文 ...

最新文章

  1. python 数据分析学什么-如何在业余时学数据分析?
  2. 小数 ###_C#中的小数关键字
  3. 安卓前端布局Android,Android开发的几种常见布局
  4. linux部署多个tomcat服务,Linux 一台服务器部署多个tomcat
  5. java面试题_Java面试题总结(2020年多家公司整理的300道Java面试题手册)
  6. 万亿“中植系”掌门人、毛阿敏丈夫离世,享年61岁,身家260亿
  7. 4.串口操作之API篇 CreateFile
  8. Xampp的apache无法启动时的解决办法
  9. 轻松转换矢量图的小工具Vector Magic
  10. 下载微信支付sdk的两个方法
  11. 字符串转换,大写变小写,小写变大写
  12. 基于MATLAB的电弧仿真模型(Mayr/Cassie 电弧模型)
  13. ai新视觉:一键解决模糊图片高清精准修复
  14. 单点登录SSO(Single Sign On)
  15. Android 6.0以下检测摄像头权限
  16. allegro库函数 c语言,[转载]Allegro之Dev-Cpp版上手指南
  17. vim复制、粘贴以及配置总结
  18. android 低电量,如何在Android设备中模拟低电量
  19. some以及every的区别
  20. Unmapped Spring configuration files found.Please configure Spring facet.”解决办法

热门文章

  1. 安卓学习笔记25:常用控件 - 下拉列表
  2. Scala学习笔记05:函数
  3. 【BZOJ3152】组合子逻辑,贪心+堆
  4. linux免密后还是要输密码,ssh配置免密后依然需要输入密码的问题解决及排查过程...
  5. bzoj1967 [AHOI2005]穿越磁场 离散最短路
  6. 【英语学习】【WOTD】animadversion 释义/词源/示例
  7. 【英语学习】【Daily English】U05 Places L04 Can I have some painkillers?
  8. 【英语学习】【WOTD】largesse 释义/词源/示例
  9. 【英语学习】【WOTD】liaison 释义/词源/示例
  10. 基于 Spring 实现管道模式的最佳实践