服务熔断降级

article/2025/9/25 17:40:12

之前给大家讲了很多服务之前的关系,今天主要给大家介绍,当服务出现问题时,我们该如何解决。

首先我们先进行一些场景分析:

  • 场景一

在这里插入图片描述

服务提供端提供了A、B、C、D 4个服务,服务调用方调用服务时,D服务出现了问题,导致调用服务D的请求都出现了超时或者错误,从而进一步影响了整个请求队列的性能。

  • 场景二

在这里插入图片描述

我们根据client1和client2 的请求压力确定了我们serverA的负载情况,但是当client2的请求从1500 增加到15000 时,会远远大于ServerA 的性能瓶颈,这个时候也会导致Client1的请求出现超时或者失败的情况。

总结一下,会有哪些情形导致我们服务出现不可以,需要我们做容错呢?

  • 单个节点故障,可能被无限向上放大
  • 多租户相互影响
  • 瞬时流量激增,系统扛不住

针对上面总结的场景,我们有哪些解决方案呢?

  • 资源隔离
  • 熔断
  • 降级

1、资源隔离

资源隔离主要是对线程资源进行隔离。这里有两种方法进行隔离:使用线程池和信号量

(1)线程池隔离

使用线程池进行资源隔离,我们有两种途径,一种是在服务端,根据不同的请求类型划分不同的队列进行处理,比如我们可以根据业务的优先级,创建高中低、默认等优先级队列来处理业务请求。

在这里插入图片描述

还有一种方式是通过在服务调用端进行队列划分,比如我们有多个服务进行调研,每个调用内部都有一个队列,通过连接管理的方式进行服务调用

在这里插入图片描述

(2)信号量

还有一种方式是通过信号量的方式来实现。我们可以通过给每个服务提供方设置可用的信号量来实现资源隔离。

在这里插入图片描述

(3)对比

信号量与线程池对比

对比项线程切换异步超时熔断限流开销
信号量
线程池

下面我们来具体分析下服务熔断和降级的设计应用。

2、服务熔断

什么叫服务熔断?

服务熔断就是临时关闭对某些功能的调用,个别的业务不可用,但是系统整体可用

有哪些服务熔断的具体例子呢?

比如一些大的电商平台在双11那天关闭退款入口,关闭换头像功能等等,这些功能的关闭对于用户来说是可感知的。

在做服务熔断设计中,我们需要关注哪些点呢?

  • 可熔断服务

我们需要判断在我们的整体架构中,哪些服务是可用进行熔断的,哪些是核心业务,不能出现不可用的情况。

  • 熔断触发:触发熔断的方式有哪些
    • 主动熔断:系统管理员根据将要出现的场景,提前关闭某些服务
    • 被动熔断:系统根据服务的状态触发熔断
  • 恢复时机

3、服务降级

什么叫服务降级?

服务降级就是有损的提供服务,保证服务柔性可用。

业务中有哪些服务降级的场景呢?

比如我们在请求高峰期的时候,对于用户的个人推荐,我们返回兜底数据;计数服务返回假数据等。这些服务对于用户来说他是不易感知的。

我们在进行服务降级设计时,需要考虑到哪些方面呢?

  • 可降级服务

首先我们要确认在我们的整体架构中,哪些服务是可以采用降级的方案的,哪些场景不适合降级的方法

  • 降级方法

我们一般的降级方法有哪些,比如常见的默认返回等等

  • 降级触发

降级触发的条件是什么,现在有2种常见的情况:主动和被动

主动:管理员发现某个服务出现异常,通过手动的方式触发某个服务的熔断,这是我们需要有一个降级的方法

被动:当某个服务调用超时或者异常,采用降级方案

  • 恢复时机

触发降级服务后,什么时候恢复到原来的服务?

4、熔断降级

基于上面的描述,我们可以发现,降级熔断都是系统可用性可靠性的保障手段,为了保障服务的柔性可用。

  • 目标一致:都是从可用性和可靠性出现,为了防止系统崩溃
  • 用户体验:用户感受到某些功能的暂不可用

很多时候,降级和熔断是配置使用的

5、断路器设计

断路器

服务熔断的开关,当对下游服务调用异常量达到设定阈值后,打开断路器,触发熔断

(1)断路器的状态流转

在这里插入图片描述

  • 断路器的状态分为3个状态,closed(关闭),open(打开),half open(半打开),服务正常使用时,断路器的状态为关闭的;
  • 当服务调用在指定时间内达到一定的阈值,就会打开断路器,服务熔断;
  • 当达到关闭时间后,会将断路器的状态更新为halfopen,将少部分流量打到之前熔断的服务;
  • 如何服务调用正常,则将断路器状态更新为closed,否则更新为open

(2)阈值与统计数据

  • 阈值的数据类型
    • 我们一般采用百分比的方式,比如失败率达到多少多少我们出发熔断
  • 颗粒度
    • 我们一般是针对某个节点中的某个实例的某个方法而言,当某个方法的调用失败率达到一定的程度,我们就触发对该方法调用的熔断
  • 统计

在这里插入图片描述

数据结构:如上图,我们需要在规定的时间范围内进行统计,只需要统计最近10个slot范围内的数据,根据场景,我们的数据结构可以是链表,或者循环数组,滑动窗口伪代码实现如下:

// 整个循环数组的结构
public class BucketCircularArray {private volatile int size =10;private static int maxSize = 60;private volatile int dataLength;private Bucket[] data; private int head;private int tail;
}// 每个slot的数据结构
public class Bucket {private final long windowStart;private AtomicInteger successNum;private AtomicInteger failNum;private AtomicInteger timeoutNum;
}
public void addBucket(Bucket bucket) {data[tail] = bucket;incrementTail();
}private void incrementTail() {if (dataLength == size) {// the size changehead = (head + 1) % size;tail = (tail + 1) % size;} else {tail = (tail + 1) % size;}if (dataLength < size) {dateLength++;}
}public Bucket tail() {if (dateLength == 0) {return null;}int index = (head + dataLength -1) % dataLength;return data[index];
}public Bucket[] toArray() {ArrayList<Bucket> list = new ArrayList<Bucket>();for (int i = 0; i < dataLength; i++) {int index = (head + i) % dataLength;Bucket tmp = data[index];if (tmp != null) {list.add(tmp);}}return list.toArray(new Bucket[list.size()]);
}
// 断路器CircuitBreaker
// open -> half_open
public boolean attemptExecution() {CircuitBreakConfigMeta configData = this.srvMgrClient.getCircuitBreakConfig(serviceName, method);............} else {if (isAfterSleepWindow(configData)) {if (status.compareAndSet(Status.OPEN, Status.HALF_OPEN)) {halfOpenPassNum.set(0);return true;} else {if (halfOpenPassNum.incrementAndGet() > maxHalfOpenPassNum) return false;elsereturn true;}} else {return false;}}
}private boolean isAfterSleepWindow(CircuitBreakConfigMeta configData) {final long circuitOpenTime = circuitOpened.get();final long currentTime = System.currentTimeMillis();final long sleepWindowTime = configData.getSleepwindowInMilliseconds();return currentTime > circuitOpenTime + sleepWindowTime;
}// hald_open -> closed
public void markSuccess() {boolean flag = false;if (status.compareAndSet(Status.HALF_OPEN, Status.CLOSED)) {circuitOpened.set(-1L);flag = true;}return flag;
}// closed -> open
public boolean tryMarkOpen(int errorPercentage, Long requestNum) {circuitBreakConfigMeta configData = this.srvMgrClient.getCircuitBreakConfig(serviceName, method);if (configData != null && !configData.isForceClosed() && requestNum >= configData.getRequestVolumThreshold() && errorPecentage >= configData.getErrorThresholdPercentage()) {if (status.compareAndSet(Status.CLOSED, Status.OPEN)) {circuitOpened.set(System.currentTimeMillis());return true;}}return false;
}// half_open -> open
public void markNonSuccess() {if (status.compareAndSet(Status.HALF_OPEN, Status.OPEN)) {logger.debug("circuitBreakerName {} reset to open", circuitBreakerName);circuitOpened.set(System.currentTimeMillis());}
}
// 控制器对象:MetricTimeWindow
public MetricTimeWindow(String serviceName, String method, ScfClientService srvMgrClient, int windowLength, EventBus eventBus) {this.metricName = serviceName + "@" + method;this.serviceName = serviceName;this.method = method;this.bucketArray = new BucketCircularArray(windowLength);this.circuitBreaker = new CircuitBreaker(serviceName, method, srvMgrClient);
}// 处理事件,成功、失败、超时
public void addEvent(MetricEventType event) {Bucket bucket = getCurrentBucket();switch(event) {case success:bucket.getSuccessNum().incrementAndGet();boolean makeClosed = circuitBreaker.markSuccess();break;case fail:bucket.getFailNum().incrementAndGet();this.circuitBreaker.markNonSuccess();break;case timeout:bucket.getTimeoutNum().incrementAndGet();this.circuitBreaker.markNonSuccess();break;}
}public boolean markSuccess() {boolean flag = false;if (status.compareAndSet(Status.HALF_OPEN, Status.CLOSED)) {circuitOpened.set(-1L);flag = true;}return flag;
}public void markNonSuccess() {if (status.compareAndSet(Status.HALF_OPEN, Status.OPEN)) {logger.debug("circuitBreakerName {} reset to open", circuitBreakerName);circuitOpened.set(System.currentTimeMillis());}
}// 获取当前的bucket
public Bucket getCurrentBucket() {long currentTime = System.currentTimeMillis();lock.readLock().lock();Bucket bucket = null;try {bucket = bucketArray.tail();if (bucket != null && currentTime <= (bucket.getWindowStart() + bucketTimeSpan))return bucket;} finally {lock.readLock().unlock();}boolean createNewBucket = false;lock.writeLock().lock();try {Bucket check = bucketArray.tail();if (check != null && currentTime <= (check.getWindowStart() + bucketTimeSpan)) {bucket = check;} else {bucket = new Bucket(currentTime);bucketArray.addBucket(bucket);createNewBucket = true;}} finally {lock.writeLock().unlock();}if (createNewBucket) {dealCreateBucketEvent();}return bucket;
}private void dealCreateBucketEvent() {Bucket[] data = null;lock.readLock().lock();data = bucketArray.toArray();if (data == null || data.length == 0) {return;}long successNum = 0L;long failNum = 0L;for (int i = 0; i < data.length; i++) {Bucket tmp = data[i];successNum += tmp.getSuccessNum().get;failNum += tmp.getFailNum().get;failNum += tmp.getTimeoutNum().get;}Long reqNum = successNum + failNum;int errorPercentage = (int) ((double) failNum / (successNum + failNum) * 100);circuitBreaker.tryMarkOpen(errorPercentage, reqNum);
}

6、Hystrix应用

Hystrix 语义为“豪猪”,是由Netflix开源的一个服务隔离组件,通过服务隔离来避免由于依赖延迟、异常,引起资源耗尽导致系统不可用的解决方案。

(1)解决问题

  • 阻止某个有问题调用耗尽系统的所有线程
  • 阻止错误在分布式系统之间的传播
  • 快速识别代替请求排队
  • 错误回退、优雅的服务降级

(2)问题出现原因

  • 服务提供者不可用:某依赖不可用,请求无法正常返回
  • 重试加大流量:代码重试逻辑或客户端重试,导致流量加大
  • 服务调用者不可用:线程资源耗尽,导致服务调用者不可用

解决方案:对依赖做隔离

(3)依赖隔离

  • 包装依赖调用逻辑,每个命令在单独线程中执行
  • 可配置依赖调用超时时间,当调用超时时,执行fallback逻辑
  • 线程池已满调用将被立即拒绝,快速失败判断
  • 依赖调用请求失败(异常、拒绝、超时、断路)时执行fallback逻辑
  • 提供熔断器组件,可以自动运行或手动调用

(4)主要特性

  • 资源隔离:限制调用服务使用的资源,当某一下游服务出现问题时,不会影响整个调用链
  • 熔断机制:当失败率达到阈值自动触发熔断,熔断器触发不在进行调用
  • 降级机制:超时、资源或触发熔断后,调用预设的降级接口返回托底数据

(5)Hystrix执行逻辑

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-EQYIXzA8-1636439807520)(/Users/fly.liu/Library/Application Support/typora-user-images/image-20211109143419855.png)]


http://chatgpt.dhexx.cn/article/5wsSPRsX.shtml

相关文章

Hystrix 服务熔断

目录 服务雪崩 一、什么是Hystrix 二、服务熔断 案例 三、服务降级 什么是服务降级 降级工厂类 设置fallbackFactory 开启feign.hystrix 四、服务熔断和降级的区别 五、DashBoard流监控 添加依赖 分布式系统面临的问题&#xff1a; 复杂分布式体系结构中的应用程序…

sentinel 熔断降级

sentinel 熔断降级 官网&#xff1a;https://sentinelguard.io/zh-cn/docs/circuit-breaking.html 熔断降级 熔断降级&#xff1a;服务由于响应慢、异常等原因触发熔断策略后&#xff0c;快速失败&#xff0c;避免线程堆积造成服务雪崩&#xff08;熔断降级通常在调用端配置&am…

kong网关熔断插件

Request Termination经常被作为kong的熔断器使用。以下在自建的konga管理界面里进行了测试配置 配置参数如下&#xff1a; 客户端发起请求&#xff0c;可以通过response进行验证&#xff0c;可以看到响应的状态码和报文都能生效。 以上配置是基于kong 0.12.3&#xff0c;可…

服务熔断的实现

# 0.服务熔断的实现思路 - 引入hystrix依赖,并开启熔断器(断路器) - 模拟降级方法 - 进行调用测试 # 1.项目中引入hystrix依赖 <!--引入hystrix--> <dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-start…

熔断

我们知道&#xff0c;如果一个软件系统的并发请求数目超过了系统的最佳线程数&#xff0c;那么就会导致激烈的资源竞争&#xff0c;随着资源的匮乏甚至枯竭&#xff0c;整个系统也就面临着灾难。所以&#xff0c;很多软件系统为了保证即使在出现并发用户数>最佳线程数时&…

什么是服务熔断?

一、什么是服务熔断&#xff1f; 考试过程中当断则断的方式&#xff0c;正好符合微服务架构中的一种安全机制&#xff1a;【熔断】 熔断这一概念来源于电子工程中的断路器&#xff08;Circuit Breaker&#xff09;。 在互联网系统中&#xff0c;当下游服务因访问压力过大而响应…

熔断原理分析与源码解读

熔断机制&#xff08;Circuit Breaker&#xff09;指的是在股票市场的交易时间中&#xff0c;当价格的波动幅度达到某一个限定的目标&#xff08;熔断点&#xff09;时&#xff0c;对其暂停交易一段时间的机制。此机制如同保险丝在电流过大时候熔断&#xff0c;故而得名。熔断机…

【C++程序设计语言A视频教程 全12讲 中科院】【下载链接】

C程序设计语言A视频教程 全12讲 中科院 这个是我在淘宝上面买的 杨力祥老师的教程~~ 奉献给大家~~~ *************************************************************************************************************************************** 下面是网上对杨力祥老师的…

国科大杨力祥老师操作系统答案总结

基于网上搜索的版本以及历届师兄的版本&#xff0c;进行了整合和修改 对应参考书籍如下&#xff0c;对应P页数也是指该书的页数。 1.为什么开始启动计算机的时候&#xff0c;执行的是BIOS代码而不是操作系统自身的代码&#xff1f; 最开始启动计算机的时候&#xff0c;计算机…

简述Mean shift 算法及其实现

文章目录 Mean shift 是什么Mean shift 算法的预备知识什么是特征什么是特征空间什么是核密度估计核函数的表示 Mean shift 算法Mean shift算法的公式推导Mean shift算法的流程Mean shift算法图示 Mean shift 算法应用Mean Shift 算法应用在聚类Mean Shift 算法图像分割 Mean s…

Johnson-Trotter算法求全排列

下面我将贴出Johnson-Trotter算法的JAVA代码 package JT;import java.util.Scanner;public class Johnson_Trotter {//求最大的移动元素public static int maxk(int n, int[] array, boolean[] f) {//k存储最大移动元素的下标int k -1, max 0;for(int i 0; i < n; i) {/…

全源最短路Johnson算法

最短路Johnson算法( O ( n m l o g m ) O(nmlogm) O(nmlogm)) 可以求任意两点最短路&#xff0c; 新图的边权改造为&#xff1a; w ( x , y ) h ( x ) − h ( y ) w(x,y)h(x)-h(y) w(x,y)h(x)−h(y) 构造的新图 d 1 ( x , y ) d ( x , y ) h ( x ) − h ( y ) d1(x,y)d(x,y…

流水线作业调度问题-动态规划(运用Johnson算法)

问题描述 n个作业{1&#xff0c;2&#xff0c;…&#xff0c;n},要在由机器M1和M2组成的流水线上完成加工。每个作业加工的顺序都是先在M1上加工&#xff0c;然后在M2上加工。M1和M2加工作业i所需的时间分别为ai和bi。 要求确定这n个作业的最优加工顺序&#xff0c;使得从第一…

【随机算法】Johnson-Lindenstrauss Theorem 详细解读

前言 最近经常接触降维, 主要是做图像处理和视频处理的维度实在是比较多, 降维这个可真是真正的技术活儿, 而且在不同情况下降维的选择至关重要, 可以说会影响到最终的结果,今天主要是详细讲解一下其中一种当今的降维准则. Johnson-Lindenstrauss Theorem的问题定义 首先, JL要…

最短路径算法--Dijkstra算法,Bellmanford算法,Floyd算法,Johnson算法

最短路径算法 在交通地图上&#xff0c;两地点之间的路径通常标有长度&#xff0c;我们可以用加权有向来描述地图上的交通网。加权有向图中每条路径都有一个路径权值&#xff0c;大小为该路径上所有边的权值之和。本节将重点讨论顶点之间最短路径问题。在实际问题中&#xff0c…

在有向图中找出所有简单环--Johnson算法

注&#xff1a;本算法和计算图所有结点对最短路径的Johnson算法不同。 目录 综述 代码解析 实例解析 引用 综述 Johnson算法由B. Johnson发表于1975年&#xff0c;用于在一个有向图中寻找所有简单环。时间复杂度上界为O((ne)(c1))&#xff0c;空间复杂度为O(ne)&#xff0…

C#,图论与图算法,寻找加权有向图中所有顶点对之间的最短路径的约翰逊算法(Johnson‘s Algorithm)与源程序

一、最短路径问题 问题是找到给定加权有向图中每对顶点之间的最短路径&#xff0c;权重可能为负。我们已经讨论了这个问题的Floyd-Warshall算法。Floyd-Warshall算法的时间复杂度为Θ&#xff08;V3&#xff09;。利用Johnson算法&#xff0c;我们可以在O&#xff08;V2log VV…

最短路径算法——Dijkstra,Bellman-Ford,Floyd-Warshall,Johnson

本文内容框架&#xff1a; 1 Dijkstra算法 2 Bellman-Ford算法 3 Floyd-Warshall算法 4 Johnson算算法 5 问题归约 6 小结 常用的最短路径算法有&#xff1a;Dijkstra算法、Bellman-Ford算法、Floyd-Warshall算法、Johnson算法 最短路径算法可以分为单源点最短路径和全源最短路…

0018算法笔记——【动态规划】流水作业调度问题与Johnson法则

1、问题描述&#xff1a; n个作业{1&#xff0c;2&#xff0c;…&#xff0c;n}要在由2台机器M1和M2组成的流水线上完成加工。每个作业加工的顺序都是先在M1上加工&#xff0c;然后在M2上加工。M1和M2加工作业i所需的时间分别为ai和bi。流水作业调度问题要求确定这n个作业的…

转:johnson算法的现实意义

Johnson算法是一种用于解决边数与节点数之间关系为O(n^2)的带权图的最短路径问题的算法。它是一种结合了Dijkstra算法和Bellman-Ford算法的技术&#xff0c;通过使用一个负权重的环检测器来消除负权重的影响。这种算法的时间复杂度为O(n^2m log n)。 Johnson算法是一种用于解决…