【微服务】Feign 整合 Sentinel,深入探索 Sentinel 的隔离和熔断降级规则,以及授权规则和自定义异常返回结果

文章目录

  • 前言
  • 一、Feign 整合 Sentinel
    • 1.1 实现步骤
    • 1.2 FallbackFactory 示例
  • 二、Sentinel 实现隔离
    • 2.1 隔离的实现方法
    • 2.2 Sentinel 实现线程隔离示例
  • 三、熔断降级规则
    • 3.1 熔断降级原理及其流程
    • 3.2 熔断策略 —— 慢调用
    • 3.3 熔断策略 —— 异常比例和异常数
  • 四、授权规则
    • 4.1 什么是授权规则
    • 4.2 授权规则示例
  • 五、自定义异常返回结果


前言

在前文中,介绍了 Sentinel 的流控模式和流控效果,然而限流只是一种预防措施,虽然可以尽量避免因为并发问题而引起的服务故障,但服务仍然可能因其他因素而发生故障。为了将这些故障控制在一定范围内,以避免雪崩效应的发生,我们需要依赖线程隔离(舱壁模式)和熔断降级机制。

无论是线程隔离还是熔断降级,它们都是为了保护客户端(调用方)免受服务故障的影响。

Sentinel保护客户端

在微服务之间的调用通常依赖于 Open Feign,因此我们首先需要将Feign与 Sentinel进行有效整合。

本文将探讨 Feign 如何与 Sentinel 整合,以及 Sentinel 的隔离、熔断降级规则以及授权规则等关键概念。

一、Feign 整合 Sentinel

1.1 实现步骤

在 Spring Cloud 中,微服务之间的调用通常依赖于 Feign 来实现。要在微服务架构中保护客户端,需要将 Feign 和 Sentinel 整合在一起。以下是将 Feign 与 Sentinel 整合的步骤,以一个名为 cloud-demo 的微服务案例为例:

1. 修改 order-serviceapplication.yml 文件,启用 Feign 对 Sentinel 的支持:

feign:sentinel:enabled: true # 启用 Feign 对 Sentinel 的支持

通过这个配置,我们告诉 Feign 在进行远程调用时要与 Sentinel 一起工作,以确保客户端受到适当的保护。

2. 编写调用失败后的降级逻辑:

当远程调用失败时,可以实现降级逻辑。有两种方式可供选择:

  • 方式一:FallbackClass,FallbackClass 是 Feign 的一种直接降级处理机制。它涉及创建一个实现原始Feign接口的类,并在该类的方法中定义降级逻辑。但是这种方式对远程调用的异常无法进行处理。
  • 方式二:FallbackFactory(降级工厂),FallbackFactory 提供了更灵活的处理远程服务调用失败的方式。它允许我们动态创建 Feign 接口的降级实例,并获取特定的异常信息。

1.2 FallbackFactory 示例

在接下来的部分,我们将深入研究如何使用 FallbackFactory 来处理远程调用的异常,并为客户端提供更好的降级体验。

步骤一:在 feign-api 模块中创建一个UserClientFallbackFactory类,实现FallbackFactory接口,并重写 create 方法:

@Slf4j
public class UserClientFallbackFactory implements FallbackFactory<UserClient> {@Overridepublic UserClient create(Throwable throwable) {return new UserClient() {@Overridepublic User findById(Long id) {log.error("查询用户失败!", throwable);return new User();}};}
}

这段代码将在 order-service 调用 user-service 失败后自动调用,在控制台会输出错误日志,并返回一个空的 User 对象。

步骤二:在 config 中将 UserClientFallbackFactory 类注册为一个 Bean:

@Bean
public UserClientFallbackFactory userClientFallbackFactory(){return new UserClientFallbackFactory();
}

步骤三:在 feignUserClient 接口的@FeignClient 注解中指定 fallbackFactory UserClientFallbackFactory

@FeignClient(value = "userservice", fallbackFactory = UserClientFallbackFactory.class) 
public interface UserClient {@GetMapping("/user/{id}")User findById(@PathVariable("id") Long id);
}

通过这些步骤,就可以使用 FallbackFactory 处理远程服务调用失败,捕获异常信息,并提供更好的降级体验。

二、Sentinel 实现隔离

在 Sentinel 中,隔离有两种主要实现方法,即信号量隔离和线程隔离。其中默认采用的是信号量隔离:

Sentinel隔离方法

2.1 隔离的实现方法

信号量隔离:

信号量隔离是一种资源隔离方式,它通过设置每个资源(或接口)的许可证数量来控制并发访问。当并发请求到达时,如果资源的许可证数量已经用尽,新的请求将被阻塞,以保护资源不被过度访问。信号量隔离适用于需要控制并发访问的场景,例如数据库连接、外部API调用等。

优点

  • 轻量级,无额外开销
  • 适用于高频调用和高扇出场景

缺点

  • 不支持主动超时
  • 不支持异步调用

场景:适用于高频调用和高扇出的场景,其中资源隔离较为轻量。

线程隔离:

线程隔离是一种资源隔离方式,它将不同的资源请求隔离到不同的线程池中执行,以确保它们不会相互影响。每个线程池负责执行特定资源的请求,如果一个请求由于某种原因导致线程阻塞或异常,不会影响其他资源的请求。线程隔离适用于需要独立线程执行的场景,例如耗时操作、阻塞调用等。

优点

  • 支持主动超时
  • 支持异步调用

缺点

  • 线程的额外开销较大

场景:线程隔离适用于低扇出场景,其中资源隔离更为重要,或需要支持异步调用和主动超时的情况。

选择隔离的实现方法取决于具体需求和应用场景。根据不同的场景和资源调用特点,可以灵活选择信号量隔离或线程隔离,以保障系统的稳定性和性能。

2.2 Sentinel 实现线程隔离示例

回顾在使用 Sentinel 控制台设置限流规则的时候,发现有两种阈值类型:

  • QPS: 就是每秒的请求数,在快速入门中已经演示过
  • 线程数: 是该资源能使用用的tomcat线程数的最大值。也就是通过限制线程数量,实现舱壁模式。

下面是一个示例,演示如何在 Sentinel 的控制台中实现线程隔离的设置。

1. 给 UserClient 的查询用户接口设置流控规则,线程数不能超过 2。

2. 然后利用 JMeter 测试。

设置线程数为 10:

设置 HTTP 请求:

启动 JMeter:

可以发现最终 10 个线程的请求只通过了其中两个。

查看 Sentinel 控制台的实时监控:

三、熔断降级规则

3.1 熔断降级原理及其流程

熔断降级原理:

熔断降级是解决雪崩问题的重要手段,其原理是由断路器统计服务调用的异常比例和慢请求比例,如果超出阈值则会熔断该服务。具体原理如下:

  1. 关闭状态(Closed):初始状态下,断路器处于关闭状态,所有请求都会被允许访问服务。
  2. 熔断状态(Open):当服务调用的异常比例或慢请求比例超出阈值时,断路器会进入熔断状态,拦截一定比例的请求,这些请求将快速失败,不会真正访问服务。
  3. 半开状态(Half-Open):在一段时间后,断路器会进入半开状态,允许部分请求访问服务,用于测试服务是否已经恢复正常。
  4. 关闭状态(Closed)或继续熔断状态(Open):根据半开状态下的请求成功与否,断路器会决定是继续保持熔断状态还是恢复到关闭状态。

熔断降级流程:

熔断降级的流程如下图所示:

熔断降级流程

流程说明如下:

  1. 断路器初始处于 Closed 状态,允许所有请求访问服务。
  2. 当服务调用失败次数或慢请求比例达到阈值,断路器进入 Open 状态,拦截所有请求,快速失败。
  3. 在一段时间后,断路器进入 Half-Open 状态,允许部分请求访问服务,用于测试服务是否已经恢复正常。
  4. 如果在 Half-Open 状态下的请求成功,则断路器进入 Closed 状态,允许所有请求访问服务。
  5. 如果在 Half-Open 状态下的请求仍然失败,断路器继续保持 Open 状态,直到下一次尝试进入 Half-Open 状态。

熔断降级通过这种状态机实现,可以帮助服务在异常情况下避免雪崩效应,提高系统的可用性和稳定性。

3.2 熔断策略 —— 慢调用

断路器熔断策略有三种:慢调用、异常比例、异常数。

慢调用就是当业务的响应时长(RT)大于指定时长的请求认定为慢调用请求。在指定时间内,如果请求数量超过设定的最小数量,慢调用比例大于设定的阈值,则触发熔断。

例如,通过 Sentinel 控制台设置慢调用降级策略:

说明:
当 RT 超过 500ms 的调用就是慢调用,统计最近 10000ms 内的请求,如果请求量超过 5 次,并且慢调用比例不低于 0.5,则触发熔断,熔断时长为 5 秒。然后进入 Half-open 状态,放行一次请求做测试。

现在有一个需求:就是给 UserClient 的查询用户接口设置降级规则,慢调用的 RT 阈值为 50ms,统计时间为 1 秒,最小请求数量为 5,失败阈值比例为 0.4,熔断时长为 5:

为了触发慢调用规则,我们需要修改UserService中的业务,增加业务耗时:

@GetMapping("/{id}")
public User queryById(@PathVariable("id") Long id ) throws InterruptedException {if(id == 1){// 休眠,触发慢调用熔断策略Thread.sleep(60);}return userService.queryById(id);
}

重启 user-service 服务后,我们可以通过快速刷新浏览器,来触发这个熔断机制:

当触发了容器之后,服务其他 ID 的接口,也会被熔断:

3.3 熔断策略 —— 异常比例和异常数

异常比例或异常数都是统计指定时间内的调用,如果调用次数超过指定请求数,并且出现异常的比例达到设定的比例阈值(或超过指定异常数),则触发熔断。

同样可以通过 Sentinel 控制台进行设置:

说明:

统计最近 1000ms 内的请求,如果请求量超过 10 次,并且异常比例不低于 0.5,则触发熔断,熔断时长为5秒。然后进入Half-open状态,放行一次请求做测试。

下面以异常比例为例:

例如现在有一个需求:就是给 UserClient 的查询用户接口设置降级规则,统计时间为 1 秒,最小请求数量为 5,失败阈值比例为 0.4,熔断时长为 5s:

为了触发异常统计,同样需要修改UserService中的业务,抛出异常:

@GetMapping("/{id}")
public User queryById(@PathVariable("id") Long id) throws InterruptedException {if(id == 1){// 休眠,触发慢调用熔断策略Thread.sleep(60);} else if (id == 2) {throw new RuntimeException("演示触发异常比例熔断!");}return userService.queryById(id);
}

此时,访问 ID 为 2 的用户就会抛出异常。

重启 user-service 服务后,我们可以通过快速刷新浏览器,来触发这个熔断机制:

四、授权规则

4.1 什么是授权规则

授权规则用于对调用方的来源进行控制,通常分为白名单和黑名单两种方式:

  • 白名单:将指定的来源(origin)加入白名单,允许这些调用者访问服务。
  • 黑名单:将指定的来源(origin)加入黑名单,不允许这些调用者访问服务。

在 Sentinel 控制台中,可以配置授权规则,如下所示:

授权规则配置

现在,我们可以从浏览器和网关两个路径去服务 order-service服务:

如果现在需要限定只允许从网关来的请求访问 order-service服务,那么流控应用中就填写网关的名称。

4.2 授权规则示例

下面将演示如何通过 Sentinel 的授权规则来限制对 order-service 服务的请求只能来自网关 gateway

Sentinel 是通过 RequestOriginParser 这个接口的 parseOrigin 来获取请求的来源的:

public interface RequestOriginParser {    // 从请求request对象中获取origin,获取方式自定义String parseOrigin(HttpServletRequest request);}

RequestOriginParser 是 Sentinel 提供的接口,用于解析请求的来源(origin)。这接口定义了一个方法 parseOrigin,我们需要实现这个方法,以自定义方式从请求对象中获取请求的来源信息。

因此,我们尝试从request中获取一个名为origin的请求头,作为origin的值,作为判断请求是否来源于网关的依据。实现的步骤如下:

  1. 首先,在gateway服务的 application.yml 文件中,利用网关的过滤器给所有的请求都添加一个名为 gatewayorigin 请求头:
spring:cloud:gateway:default-filters: # 默认过滤器,会对所有的路由请求都生效- AddRequestHeader=origin, gateway # Sentinel 授权规则,只有从网关服务的才合法,通过添加请求头标识
  1. 然后在 Sentinel 控制台中添加授权规则,指定流控应用为 gateway

  1. order-service 中实现一个 HeadOriginParser 类,实现 RequestOriginParser 接口,用来获取请求源:
@Component
public class HeadOriginParser implements RequestOriginParser {@Overridepublic String parseOrigin(HttpServletRequest httpServletRequest) {// 1. 获取请求头String origin = httpServletRequest.getHeader("origin");// 2. 非空判断if (StringUtils.isEmpty(origin)) {return "blank";}return origin;}
}

如果不存在origin 这个字段的请求头,直接返回"blank",否则直接返回 origin 的值,然后交给 Sentinel 进行判断请求源。

  1. 重启 order-servicegateway 服务,进行演示:

此时,如果我们之间通过浏览器访问 order-service 的接口,发现就被禁止访问了:

如果通过 gateway 网关,就能够成功访问了:

五、自定义异常返回结果

默认情况下,发生限流、降级、授权拦截时,都会抛出异常到调用方,但是通过上面所有的例子,我们发现都只返回了一种结果,那就是“Blocked by Sentinel (flow limiting)”。如果我们想要知道微服务调用失败的具体原因,就需要对异常进行自定义处理:

如果要实现对 Sentinel 的异常实现自定义,那么就需要实现 BlockExceptionHandler 接口,它是 Sentinel 提供的一个接口,用于自定义处理调用失败时的异常情况。

而关于 BlockException这个类,包含很多个子类,分别对应不同的场景:

异常说明
FlowException限流异常
ParamFlowException热点参数限流的异常
DegradeException降级异常
AuthorityException授权规则异常
SystemBlockException系统规则异常

我们可以通过这些子类来判断在调用微服务失败的时候,具体出现了哪种情况,然后通过自定义异常进行结果的返回,下面是一个实现自定义异常的代码示例:

@Component
public class SentinelExceptionHandler implements BlockExceptionHandler {@Overridepublic void handle(HttpServletRequest request, HttpServletResponse response, BlockException e) throws Exception {String msg = "未知异常";int status = 429;if (e instanceof FlowException) {msg = "请求被限流了";} else if (e instanceof ParamFlowException) {msg = "请求被热点参数限流";} else if (e instanceof DegradeException) {msg = "请求被降级了";} else if (e instanceof AuthorityException) {msg = "没有权限访问";status = 401;}response.setContentType("application/json;charset=utf-8");response.setStatus(status);response.getWriter().println("{\"msg\": " + msg + ", \"status\": " + status + "}");}
}

在上述示例中,创建了一个名为 CustomBlockExceptionHandler 的自定义异常处理类,实现了 BlockExceptionHandler 接口。在重写的 handle 方法中,根据不同的 BlockException 类型来确定异常消息和状态码,然后将这些信息返回给调用方。

例如,在上面配置了授权规则的情况下,直接通过浏览器访问 order-service 服务:

此时,就能够清楚的知道微服务调用失败的原因了。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/116039.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

数据结构中的七大排序(Java实现)

目录 一、直接插入排序 二、希尔排序 三、直接选择排序 四、堆排序 五、冒泡排序 六、快速排序 七、归并排序 一、直接插入排序 思想&#xff1a; 定义i下标之前的元素全部已经有序&#xff0c;遍历一遍要排序的数组&#xff0c;把i下标前的元素全部进行排序&#xff0…

ArGIS Engine专题(15)之GP模型在地图服务与地图服务之间实现叠置分析

前一篇文章已经介绍过导入要素范围与地图服务的叠加分析,相当于单要素与多要素之间的分析,这篇文章介绍地图服务与地图服务之间的叠加分析,即是多要素有多要素之间的相交分析,功能基本类似。 一、结果预览 二、需求简介 以下是一些常见的业务场景: (1)空间规划和土地…

电脑技巧:27个Office使用小技巧,值得收藏

目录 一、Word 二、EXCEL 三、附文&#xff1a;Word和Excel快捷键 我们中的绝大部分人都使用微软的Office&#xff0c;但是我们是否都了解如何能够最有效地使用它&#xff1f;我们在这里列举了一些关于使用Word和Excel的窍门。 我们使用最多的软件可能就是办公软件了——字…

思辨:移动开发的未来在哪?

前段时间在知乎看到关于移动开发未来的问题&#xff0c;就尝试回答了一下&#xff0c;也触发了我对移动开发未来的思考。 什么是移动开发&#xff1f; 我们口中说的移动开发是什么&#xff0c;从广义和狭义的角度分别来看下&#xff1a; 从广义角度来看&#xff0c;移动开发是…

docker-rabbitmq 安装依赖

出现的问题如下: channel error; protocol method: #method(reply-code404, reply-textNOT_FOUND - no channel error&#xff1b; protocol method: #method&#xff1c;channel.close&#xff1e;(reply-code404, reply-textNOT_FOUND - no 查看rabbitmq 客户端是否存在如…

Kubernetes - 一键安装部署 K8S(附:Kubernetes Dashboard)

问题描述 不知道大伙是如何安装 K8s&#xff0c;特别还是集群的时候&#xff0c;我上一次安装搭建的时候&#xff0c;那个恶心到我了&#xff0c;真的是一步一个脚印走完整个搭建流程&#xff0c;爬了不少坑。 于是&#xff0c;才有了今天的文章&#xff0c;到底有没有可以一…

Kafka快速入门(最新版3.6.0)

文章目录 一、初识MQ1.1 什么是MQ1.2 同步和异步通讯1.1.1 同步通讯1.1.2 异步通讯 1.3 技术对比1.4 MQ的两种模式 二、初识Kafka2.1 Kafka的使用场景2.2 Kafka基本概念2.3 Topic与Partition 三、Kafka基本使用3.1 部署前的准备3.2 启动kafka服务器3.3 Kafka核心概念之Topic3.4…

出差学小白知识No6:LD_PRELOAD变量路径不对找不到库文件

交叉编译的时候出现以下问题&#xff0c;显示LD_PRELOAD变量找不到路劲 首先先查看一下LD_PRELOAD的路径&#xff1a;echo $LD_PRELOAD 如果输出一大串&#xff0c;那么先进行清空&#xff1a;unset LD_PRELOAD 重新给LD_PRELOAD进行赋值他的路径和库文件&#xff1a; expor…

自然语言处理---RNN经典案例之构建人名分类器

1 案例介绍 关于人名分类问题&#xff1a;以一个人名为输入, 使用模型帮助判断它最有可能是来自哪一个国家的人名&#xff0c;这在某些国际化公司的业务中具有重要意义&#xff0c;在用户注册过程中&#xff0c;会根据用户填写的名字直接给他分配可能的国家或地区选项&#xff…

【第三天】C++类和对象进阶指南:从堆区空间操作到友元的深度掌握

一、new和delete 堆区空间操作 1、new和delete操作基本类型的空间 new与C语言中malloc、delete和C语言中free 作用基本相同 区别&#xff1a; new 不用强制类型转换 new在申请空间的时候可以 初始化空间内容 2、 new申请基本类型的数组 3、new和delete操作类的空间 4、new申请…

为什么需要it企业知识库?it企业知识库能带来什么?

在企业运营过程中&#xff0c;会产生大量的经营数据、管理规范、资料和文档等数据&#xff0c;但这些数据的产生时间和空间碎片化&#xff0c;数据来源和结构多种多样&#xff0c;信息关系也较为复杂。 it企业知识库 正是因为这些问题的存在&#xff0c;导致了企业信息管理零散…

【word技巧】word页眉,如何禁止他人修改?

我们设置了页眉内容之后&#xff0c;不想其他人修改自己的页眉内容&#xff0c;我们可以设置加密的&#xff0c;设置方法如下&#xff1a; 先将页眉设置好&#xff0c;退出页眉设置之后&#xff0c;我们选择布局功能&#xff0c;点击分隔符 – 连续 设置完之后页面分为上下两节…

基于OpenAPI、freemarker动态生成swagger文档

前言 spring项目中可以使用springfox或者springdoc&#xff0c;通过写注解的方式生成swagger文档&#xff0c;下面介绍一种不写注解&#xff0c;动态生成swagger文档的方式&#xff0c;在某些场景会适用&#xff0c;例如接口是动态生成的&#xff0c;此时swagger就不能通过注解…

Qt中QFile、QByteArray QDataStream和QTextStream区别及示例

在Qt中&#xff0c;QFile、QByteArray、QDataStream和QTextStream是常用的文件和数据处理类。 主要功能和区别 QFile&#xff1a; QFile是用于读写文本和二进制文件以及资源的I/O设备。可以单独使用QFile&#xff0c;或者更方便地与QTextStream或QDataStream一起使用。 通常在…

Cookie和Session

前言扩展&#xff1a;笔者在之前学习前端扫盲阶段&#xff0c;写过一个简单易读的&#xff1a;表白墙项目&#xff0c;具体的前端代码如下&#xff1a; <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta ht…

mac安装+配置python3环境

一、python3下载 官网下载 https://www.python.org/ 二、python3安装 打开下载好的.pkg文件一直继续确认即可。 三、验证是否安装成功 打开终端&#xff0c;输入python3&#xff0c;如果返回python对应的版本信息&#xff0c;则安装成功。 四、python配置 如果需要输入…

基于卷积优化优化的BP神经网络(分类应用) - 附代码

基于卷积优化优化的BP神经网络&#xff08;分类应用&#xff09; - 附代码 文章目录 基于卷积优化优化的BP神经网络&#xff08;分类应用&#xff09; - 附代码1.鸢尾花iris数据介绍2.数据集整理3.卷积优化优化BP神经网络3.1 BP神经网络参数设置3.2 卷积优化算法应用 4.测试结果…

Qt窗体设计的布局

本文介绍Qt窗体的布局。 Qt窗体的布局分为手动布局和自动布局&#xff0c;手动布局即靠手工排布各控件的位置。而自动布局则是根据选择的布局类型自动按此类型排布各控件的位置&#xff0c;使用起来比较方便&#xff0c;本文主要介绍Qt的自动布局。 1.垂直布局 垂直布局就是…

基于WebRTC构建的程序因虚拟内存不足导致闪退问题的排查以及解决办法的探究

目录 1、WebRTC简介 2、问题现象描述 3、将Windbg附加到目标进程上分析 3.1、Windbg没有附加到主程序进程上&#xff0c;没有感知到异常或中断 3.2、Windbg感知到了中断&#xff0c;中断在DebugBreak函数调用上 3.3、32位进程用户态虚拟地址和内核态虚拟地址的划分 …

义乌再次位列第一档!2022年跨境电商综试区评估结果揭晓!

义乌跨境电商综试区捷报频传&#xff0c;在商务部公布的“2022年跨境电子商务综合试验区评估”结果中&#xff0c;中国&#xff08;义乌&#xff09;跨境电子商务综合试验区&#xff08;以下简称&#xff1a;“跨境综试区”&#xff09;评估结果为成效明显&#xff0c;综合排名…