在分布式系统中,如果某个服务节点发生故障或者网络发生异常,都有可能导致调用方被阻塞等待,如果超时时间设置很长,调用方资源很可能被耗尽。这又导致了调用方的上游系统发生资源耗尽的情况,最终导致系统雪崩。

如下图:

如果D服务发生了故障不能响应,B服务调用D时只能阻塞等待。假如B服务调用D服务设置超时时间是10秒,请求速率是每秒100个,那10秒内就会有1000个请求线程被阻塞等待,如果B的线程池大小设置1000,那B系统因为线程资源耗尽已经不能对外提供服务了。而这又影响了入口系统A的服务,最终导致系统全面崩溃。

提高系统的整体容错能力是防止系统雪崩的有效手段。

在Martin Fowler和James Lewis的文章 《Microservices: a definition of this new architectural term》[1]中,提出了微服务的9个特征,其中一个是容错设计。

要防止系统发生雪崩,就必须要有容错设计。如果遇到突增流量,一般的做法是对非核心业务功能采用熔断和服务降级的措施来保护核心业务功能正常服务,而对于核心功能服务,则需要采用限流的措施。

今天我们来聊一聊系统容错中的限流、熔断和服务降级。

1 限流

当系统的处理能力不能应对外部请求的突增流量时,为了不让系统崩溃,必须采取限流的措施。

1.1 限流指标

1.1.1 TPS

系统吞吐量是衡量系统性能的关键指标,按照事务的完成数量来限流是最合理的。

但是对实操性来说,按照事务来限流并不现实。在分布式系统中完成一笔事务需要多个系统的配合。比如我们在电商系统购物,需要订单、库存、账户、支付等多个服务配合完成,有的服务需要异步返回,这样完成一笔事务花费的时间可能会很长。如果按照TPS来进行限流,时间粒度可能会很大大,很难准确评估系统的响应性能。

1.1.2 HPS

每秒请求数,指每秒钟服务端收到客户端的请求数量。

如果一个请求完成一笔事务,那TPS和HPS是等同的。但在分布式场景下,完成一笔事务可能需要多次请求,所以TPS和HPS指标不能等同看待。

1.1.3 QPS

服务端每秒能够响应的客户端查询请求数量。

如果后台只有一台服务器,那HPS和QPS是等同的。但是在分布式场景下,每个请求需要多个服务器配合完成响应。

目前主流的限流方法多采用HPS作为限流指标。

1.2 限流方法

1.2.1 流量计数器

这是最简单直接的方法,比如限制每秒请求数量100,超过100的请求就拒绝掉。

但是这个方法存在2个明显的问题:

  • 单位时间(比如1s)很难把控,如下图:这张图上,从下面时间看,HPS没有超过100,但是从上面看HPS超过100了。
  • 有一段时间流量超了,也不一定真的需要限流,如下图,系统HPS限制50,虽然前3s流量超了,但是如果都超时时间设置为5s,并不需要限流。

1.2.2 滑动时间窗口

滑动时间窗口算法是目前比较流行的限流算法,主要思想是把时间看做是一个向前滚动的窗口,如下图:

开始的时候,我们把t1~t5看做一个时间窗口,每个窗口1s,如果我们定的限流目标是每秒50个请求,那t1~t5这个窗口的请求总和不能超过250个。

这个窗口是滑动的,下一秒的窗口成了t2~t6,这时把t1时间片的统计抛弃,加入t6时间片进行统计。这段时间内的请求数量也不能超过250个。

滑动时间窗口的优点是解决了流量计数器算法的缺陷,但是也有2个问题:

  • 流量超过就必须抛弃或者走降级逻辑
  • 对流量控制不够精细,不能限制集中在短时间内的流量,也不能削峰填谷

1.2.3 漏桶算法

漏桶算法的思想如下图:

在客户端的请求发送到服务器之前,先用漏桶缓存起来,这个漏桶可以是一个长度固定的队列,这个队列中的请求均匀地发送到服务端。

如果客户端的请求速率太快,漏桶的队列满了,就会被拒绝掉,或者走降级处理逻辑。这样服务端就不会受到突发流量的冲击。

漏桶算法的优点是实现简单,可以使用消息队列来削峰填谷。

但是也有3个问题需要考虑:

  • 漏桶的大小,如果太大,可能给服务端带来较大处理压力,太小可能会有大量请求被丢弃。
  • 漏桶给服务端的请求发送速率。
  • 使用缓存请求的方式,会使请求响应时间变长。

漏桶大小和发送速率这2个值在项目上线初期都会根据测试结果选择一个值,但是随着架构的改进和集群的伸缩,这2个值也会随之发生改变。

1.2.4 令牌桶算法

令牌桶算法就跟病人去医院看病一样,找医生之前需要先挂号,而医院每天放的号是有限的。当天的号用完了,第二天又会放一批号。

算法的基本思想就是周期性地执行下面的流程:

客户端在发送请求时,都需要先从令牌桶中获取令牌,如果取到了,就可以把请求发送给服务端,取不到令牌,就只能被拒绝或者走服务降级的逻辑。如下图:

令牌桶算法解决了漏桶算法的问题,而且实现并不复杂,使用信号量就可以实现。在实际限流场景中使用最多,比如google的guava中就实现了令牌桶算法限流,感兴趣可以研究一下。

1.2.5 分布式限流

如果在分布式系统场景下,上面介绍的4种限流算法是否还适用呢?

以令牌桶算法为例,假如在电商系统中客户下了一笔订单,如下图:

如果我们把令牌桶单独保存在一个地方(比如redis中)供整个分布式系统用,那客户端在调用组合服务,组合服务调用订单、库存和账户服务都需要跟令牌桶交互,交互次数明显增加了很多。

有一种改进就是客户端调用组合服务之前首先获取四个令牌,调用组合服务时减去一个令牌并且传递给组合服务三个令牌,组合服务调用下面三个服务时依次消耗一个令牌。

1.2.6 hystrix限流

hystrix可以使用信号量和线程池来进行限流。

1.2.6.1 信号量限流

hystrix可以使用信号量进行限流,比如在提供服务的方法上加下面的注解。这样只能有20个并发线程来访问这个方法,超过的就被转到了errMethod这个降级方法。

@HystrixCommand(commandProperties= {@HystrixProperty(name="execution.isolation.strategy", value="SEMAPHORE"),@HystrixProperty(name="execution.isolation.semaphore.maxConcurrentRequests", value="20")},fallbackMethod = "errMethod"
)

1.2.6.2 线程池限流

hystrix也可以使用线程池进行限流,在提供服务的方法上加下面的注解,当线程数量

@HystrixCommand(commandProperties = {@HystrixProperty(name = "execution.isolation.strategy", value = "THREAD")},threadPoolKey = "createOrderThreadPool",threadPoolProperties = {@HystrixProperty(name = "coreSize", value = "20"),@HystrixProperty(name = "maxQueueSize", value = "100"),@HystrixProperty(name = "maximumSize", value = "30"),@HystrixProperty(name = "queueSizeRejectionThreshold", value = "120")},fallbackMethod = "errMethod"
)

这里要注意:在java的线程池中,如果线程数量超过coreSize,创建线程请求会优先进入队列,如果队列满了,就会继续创建线程直到线程数量达到maximumSize,之后走拒绝策略。但在hystrix配置的线程池中多了一个参数queueSizeRejectionThreshold,如果queueSizeRejectionThreshold < maxQueueSize,队列数量达到queueSizeRejectionThreshold就会走拒绝策略了,因此maximumSize失效了。如果queueSizeRejectionThreshold > maxQueueSize,队列数量达到maxQueueSize时,maximumSize是有效的,系统会继续创建线程直到数量达到maximumSize。Hytrix线程池设置坑[2]

2 熔断

相信大家对断路器并不陌生,它就相当于一个开关,打开后可以阻止流量通过。比如保险丝,当电流过大时,就会熔断,从而避免元器件损坏。

服务熔断是指调用方访问服务时通过断路器做代理进行访问,断路器会持续观察服务返回的成功、失败的状态,当失败超过设置的阈值时断路器打开,请求就不能真正地访问到服务了。

为了更好地理解,我画了下面的时序图:

2.1 断路器的状态

断路器有3种状态:

  • CLOSED:默认状态。断路器观察到请求失败比例没有达到阈值,断路器认为被代理服务状态良好。
  • OPEN:断路器观察到请求失败比例已经达到阈值,断路器认为被代理服务故障,打开开关,请求不再到达被代理的服务,而是快速失败。
  • HALF OPEN:断路器打开后,为了能自动恢复对被代理服务的访问,会切换到半开放状态,去尝试请求被代理服务以查看服务是否已经故障恢复。如果成功,会转成CLOSED状态,否则转到OPEN状态。

断路器的状态切换图如下:

2.2 需要考虑的问题

使用断路器需要考虑一些问题:

  • 针对不同的异常,定义不同的熔断后处理逻辑。
  • 设置熔断的时长,超过这个时长后切换到HALF OPEN进行重试。
  • 记录请求失败日志,供监控使用。
  • 主动重试,比如对于connection timeout造成的熔断,可以用异步线程进行网络检测,比如telenet,检测到网络畅通时切换到HALF OPEN进行重试。
  • 补偿接口,断路器可以提供补偿接口让运维人员手工关闭。
  • 重试时,可以使用之前失败的请求进行重试,但一定要注意业务上是否允许这样做。

2.3 使用场景

  • 服务故障或者升级时,让客户端快速失败
  • 失败处理逻辑容易定义
  • 响应耗时较长,客户端设置的read timeout会比较长,防止客户端大量重试请求导致的连接、线程资源不能释放

3 服务降级

前面讲了限流和熔断,相比来说,服务降级是站在系统全局的视角来考虑的。

在服务发生熔断后,一般会让请求走事先配置的处理方法,这个处理方法就是一个降级逻辑。

服务降级是对非核心、非关键的服务进行降级。

3.1 使用场景

  • 服务处理异常,把异常信息直接反馈给客户端,不再走其他逻辑
  • 服务处理异常,把请求缓存下来,给客户端返回一个中间态,事后再重试缓存的请求
  • 监控系统检测到突增流量,为了避免非核心业务功能耗费系统资源,关闭这些非核心功能
  • 数据库请求压力大,可以考虑返回缓存中的数据
  • 对于耗时的写操作,可以改为异步写
  • 暂时关闭跑批任务,以节省系统资源

3.2 使用hystrix降级

3.2.1 异常降级

hystrix降级时可以忽略某个异常,在方法上加上@HystrixCommand注解:

下面的代码定义降级方法是errMethod,对ParamErrorException和BusinessTypeException这两个异常不做降级处理。

@HystrixCommand(fallbackMethod = "errMethod",ignoreExceptions = {ParamErrorException.class, BusinessTypeException.class}
)

3.2.2 调用超时降级

专门针对调用第三方接口超时降级。

下面的方法是调用第三方接口3秒未收到响应就降级到errMethod方法。

@HystrixCommand(commandProperties = {@HystrixProperty(name="execution.timeout.enabled", value="true"),@HystrixProperty(name="execution.isolation.thread.timeoutInMilliseconds", value="3000"),},fallbackMethod = "errMethod"
)

总结

限流、熔断和服务降级是系统容错的重要设计模式,从一定意义上讲限流和熔断也是一种服务降级的手段。

熔断和服务降级主要是针对非核心业务功能,而核心业务如果流程超过预估的峰值,就需要进行限流。

对于限流,选择合理的限流算法很重要,令牌桶算法优势很明显,也是使用最多的限流算法。

在系统设计的时候,这些模式需要配合业务量的预估、性能测试的数据进行相应阈值的配置,而这些阈值最好保存在配置中心,方便实时修改。

来源:
https://mp.weixin.qq.com/mp/profile_ext?action=home&__biz=MzU2NTEyNjIzMw==&scene=161#wechat_redirect

10张图带你彻底搞懂什么是限流、熔断、服务降级相关推荐

  1. 12张图带你彻底搞懂服务限流、熔断、降级、雪崩

    目录 一.服务雪崩 二.服务限流 1.限流指标 1)TPS 2)HPS 3)QPS 2.限流方法 1)流量计数器 2)滑动时间窗口 3)漏桶算法 4)令牌桶算法 5)分布式限流 6)hystrix限流 ...

  2. 10分钟带你彻底搞懂服务限流和服务降级

    文章目录 十分钟搞懂系列 服务限流 计数器法 滑动窗口法 漏桶算法 令牌桶算法 服务降级 十分钟搞懂系列 序号 标题 链接 1 10分钟带你彻底搞懂企业服务总线 https://blog.csdn.n ...

  3. 32张图带你彻底搞懂事务和锁!

    作者 | 悟空聊架构 来源 | 悟空聊架构(ID:PassJava666) 转载请联系授权(微信ID:PassJava) 本篇主要内容如下: 本篇主要内容 一.事务 1.1 什么是事务 为单个工作单元 ...

  4. 3 万字 + 100 张图带你彻底搞懂 TCP 面试题(强烈建议收藏)

    大家好,我是小林,一个专为大家图解的工具人. 不管面试 Java .C/C++.Python 等开发岗位, TCP 的知识点可以说是必问的了. 任 TCP 虐我千百遍,我仍待 TCP 如初恋. 过去不 ...

  5. 26张图带你彻底搞懂volatile关键字

    引子 小艾吃饭路上碰上小牛,忙问:你昨天面大厂面的咋样了?听说他们最喜欢问多线程相关知识. 小牛说:对啊,第一个问题我就讲了20分钟,直接把面试官讲服了. 小艾忙问:什么问题能讲这么久?是不是问你情感 ...

  6. 6 张图带你彻底搞懂分布式事务 XA 模式

    作者 | 朱晋君 来源 | 阿里巴巴云原生公众号 XA 协议是由 X/Open 组织提出的分布式事务处理规范,主要定义了事务管理器 TM 和局部资源管理器 RM 之间的接口.目前主流的数据库,比如 o ...

  7. 多张图带你彻底搞懂DNS域名解析过程

    目录 1.DNS 2.域名系统DNS 的作用 3.域名的层级关系 4.DNS域名解析过程 递归查询 迭代查询 5.高速缓存 6.DNS相关面试问题 1.DNS DNS(Domain Name Syst ...

  8. 10分钟带你彻底搞懂负载均衡

    文章目录 十分钟搞懂系列 负载均衡是如何保证软件系统的生产部署的? 负载均衡分发策略 请求由谁来分发? 服务器端负载均衡器 客户端负载均衡 请求分发到哪去? 静态负载均衡算法 动态负载均衡算法 十分钟 ...

  9. 10分钟带你彻底搞懂微内核架构

    文章目录 十分钟搞懂系列 什么是微内核架构? 如何实现微内核架构? 总结 十分钟搞懂系列 序号 标题 链接 1 10分钟带你彻底搞懂企业服务总线 https://blog.csdn.net/belon ...

最新文章

  1. vim的简单介绍与使用
  2. a*算法的时间复杂度_算法基础——时间复杂度amp;空间复杂度
  3. 如果你在2018面试前端,那这篇文章最好看一看!
  4. 基于React脚手架集成Cesium
  5. 【设计模式】依赖倒转原则
  6. 曝光原理_简单摄影之一 曝光原理
  7. 51nod 1770 数数字 找规律,注意进位,时间复杂度O(n)
  8. 将 JAR 转为 EXE – JSMOOTH 的使用教程(第二期)(转载)
  9. 随想录(redis的学习和使用)
  10. 猜数字小c语言游戏课程任务书,C语言课程设计猜数字游戏姚成.doc
  11. SQLyog客户端 导入sql文件乱码的解决方法
  12. 有关csdn博客账号注销说明
  13. 连续状态空间模型离散化
  14. 郑大计算机组成原理(专科)试卷 答案,专科《数字电路与逻辑设计》
  15. 麦克斯韦方程组(彩图完美解释版)
  16. 走进龙芯3A3000(三)在Gentoo N64上安装xorg-server
  17. 基于java的springboot宠物商城系统毕业设计springboot开题报告
  18. BurpSuite2021 -- 目标模块(Target)
  19. 一组li或者div里面多个弹出层对应各自的内容
  20. excel 外部链接 乱码_在Excel文件中查找外部链接

热门文章

  1. 下拉数据过多,超过3000条浏览器卡顿
  2. java打印格式_Java 打印格式
  3. python将想要打印的数据输出到txt文件中,打印省略号里面的内容
  4. 参与 Spring 4 中文文档翻译
  5. 2022年辽宁省安全保护服务人员(初级保安员)考试练习题及答案
  6. 铺管家教育:拼多多区间价格设置问题
  7. Java_比较器枚举类和注解集合泛型
  8. 外网访问内网ERP【内网穿透】
  9. [肖博数学]高考数学二轮复习方法解析几何题难点突破专题
  10. 无线路由设置外网访问内网