【微服务】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.rhkb.cn/news/168344.html

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

相关文章

文件的基本操作(创建文件,删除文件,读写文件,打开文件,关闭文件)

1.创建文件(create系统调用) 1.进行Create系统调用时&#xff0c; 需要提供的几个主要参数: 1.所需的外存空间大小&#xff08;如:一个盘块&#xff0c;即1KB) 2&#xff0e;文件存放路径&#xff08;“D:/Demo”) 3.文件名&#xff08;这个地方默认为“新建文本文档.txt”) …

计算机网路第3章-运输层

概述和运输层服务 运输层协议为运行在不同主机上的应用进程提供了逻辑通信&#xff0c;从应用程序角度看&#xff0c;通过使用逻辑通信&#xff0c;就好像运行在不同主机上的进程直接相连在一起一样。 运输层和网络层的关系 网络层提供主机之间的通信&#xff0c;而运输层提…

期中考misc复现

第一题 flow analysis 1 服务器附带的后门文件名是什么&#xff1f;&#xff08;包括文件后缀&#xff09; Windows后门是指当攻击者通过某种手段已经拿到服务器的控制权之后&#xff0c;然后通过在服务器上放置一些后门&#xff08;脚本、进程、连接之类&#xff09;&#xf…

SpringBoot Lombok的使用

目录 下载Lombok插件 Lombok的用法 获取日志对象 生成get,set方法 Lombok框架的实现原理 Lombok的常用注解 下载Lombok插件 要使用Lombok首先要确保idea安装了lombok插件 在项目中添加 lombok依赖 在<dependency>里右键生成点击edit starters 插件(没有就下载,可…

分治法,动态规划法,贪心法,回溯法主要概括

目录 分治法&#xff0c;动态规划法&#xff0c;贪心法&#xff0c;回溯法主要概括 1.前言2.分治法2.1基本思想&#xff1a;2.2适用条件&#xff1a;2.3时间复杂度&#xff1a;2.4主要解决&#xff1a;2.5关键字&#xff1a;2.6其他&#xff1a; 3.动态规划法3.1基本思想&…

使用序列化技术保存数据 改进 IO流完成项目实战水果库存系统

上一节内容是 使用IO流完成项目实战水果库存系统https://blog.csdn.net/m0_65152767/article/details/133999972?spm1001.2014.3001.5501 package com.csdn.fruit.pojo; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; import java…

基于stm32控制的4G模块在设备模式下通讯

这里的32控制其实和51的控制思路都是一样的&#xff0c;都是先利用一个网络助手将家里的无线网生成局域网&#xff0c;接着通过花生壳软件将局域网变成公共网&#xff0c;最后是利用串口助手&#xff0c;在4G模块的AT指令模式写入命令ATSOCKTCPC,公共网IP地址,公共网端口号&…

Hadoop3教程(二十七):(生产调优篇)HDFS读写压测

文章目录 &#xff08;146&#xff09;HDFS压测环境准备&#xff08;147&#xff09;HDFS读写压测写压测读压测 参考文献 &#xff08;146&#xff09;HDFS压测环境准备 对开发人员来讲&#xff0c;压测这个技能很重要。 假设你刚搭建好一个集群&#xff0c;就可以直接投入生…

跨境商城源码可以支持多种用户权限管理方式吗?

在跨境电商领域&#xff0c;为了满足消费者的需求和提供更好的购物体验&#xff0c;开发人员需要使用一种可靠的跨境商城源码来构建跨境电商平台。然而&#xff0c;这个商城源码是否可以支持多种用户权限管理方式呢?本文将探讨这个问题。 1.什么是跨境商城源码? 跨境商城源码…

linux进程管理,一个进程的一生(喂饭级教学)

这篇文章谈谈linux中的进程管理。 一周爆肝&#xff0c;创作不易&#xff0c;望支持&#xff01; 希望对大家有所帮助&#xff01;记得收藏&#xff01; 要理解进程管理&#xff0c;重要的是周边问题&#xff0c;一定要知其然&#xff0c;知其所以然。看下方目录就知道都是干货…

【单片机学习笔记】Windows+Vscode+STM32F4+freeRTOS+FatFs gcc环境搭建

为摒弃在接受keil邮件&#xff0c;研究了下gun编译&#xff0c;以STM32F407为例&#xff0c;简单记录 1. 软件包准备 Git 选择对应版本直接安装即可https://git-scm.com/download/winmakegcc ​ 1&#xff09;将上述软件包放置于C盘根目录 2&#xff09;添加环境变量 3&am…

【Git】idea提交项目到Gitee

文章目录 1. 创建Gitee仓库1. 新建仓库2. 添加描述3. 复制仓库地址 2. idea建立连接提交2.1 Create Git Repository2.2 选择要提交的根目录2.3 Commit2.4 Push2.5 提交成功 1. 创建Gitee仓库 1. 新建仓库 2. 添加描述 3. 复制仓库地址 点击右上角克隆/下载&#xff0c;复制HT…

pv操作题目笔记

对于 pv 操作分以下几步走 什么是pv操作 PV操作在进程同步中通常指的是信号量&#xff08;Semaphore&#xff09;操作。信号量是一种用于控制多个并发进程或线程之间的同步和互斥访问的同步工具。PV操作通常涉及两个基本操作&#xff1a;P操作&#xff08;wait操作&#xff0…

MD5生成和校验

MD5生成和校验 2021年8月19日席锦 任何类型的一个文件&#xff0c;它都只有一个MD5值&#xff0c;并且如果这个文件被修改过或者篡改过&#xff0c;它的MD5值也将改变。因此&#xff0c;我们会对比文件的MD5值&#xff0c;来校验文件是否是有被恶意篡改过。 什么是MD5&#xff…

微信小程序实现类似于 vue中ref管理调用子组件函数的方式

微信小程序中确实有类似于 vue 中 ref管理子组件的方式、 这里 我给子组件定义了一个 class 只要是 css选择器拿得到的 都没什么问题 但你要保证唯一性 建议前端开发还是慎重一点 就算是不能重复也尽量用class 因为id总还是有风险的 然后 我在子组件中顶一个了一个函数 start…

计网----数据包在传输中的变化过程,单播组播和广播,ARP协议,ARP代理,免费ARP,DNS协议,路由数据转发过程

计网----数据包在传输中的变化过程&#xff0c;单播组播和广播&#xff0c;ARP协议&#xff0c;ARP代理&#xff0c;免费ARP&#xff0c;DNS协议&#xff0c;路由数据转发过程 一.数据包在传输中的变化过程&#xff08;在同一个路由器下&#xff09; 1.传输数据时&#xff0c…

RPA的尽头是超自动化?

超自动化在经过数年的发酵期后&#xff0c;已从一个科技概念崛起为市值近千亿元的新赛道&#xff0c;包括各大互联网巨头、科技公司都纷纷围绕超自动化进行战略布局。 一方面&#xff0c;是行业巨头选择纷纷跻身超自动化新赛道&#xff0c;另一方面&#xff0c;RPA行业的领军企…

Proteus仿真--VB上位机程序控制DS1302时钟仿真(Proteus仿真+程序)

本文主要介绍基于51单片机的VB上位机程序控制DS1302时钟仿真设计&#xff08;完整仿真源文件及代码见文末链接&#xff09; 简介 硬件电路主要分为单片机主控模块、DS1302模块、LCD1602液晶显示模块以及串口模块 &#xff08;1&#xff09;单片机主控模块&#xff1a;单片机选…

Git最佳实践:git常用命令和原理

Git 是一个开源的分布式版本控制系统。 Git 工作区、暂存区和版本库 工作区&#xff1a;就是你在电脑里能看到的目录。暂存区&#xff1a;英文叫 stage 或 index。一般存放在 .git 目录下的 index 文件&#xff08;.git/index&#xff09;中&#xff0c;所以我们把暂存区有时…

Leetcode—104.二叉树的最大深度【简单】

2023每日刷题&#xff08;六&#xff09; Leetcode—104.二叉树的最大深度 递归实现代码 /*** Definition for a binary tree node.* struct TreeNode {* int val;* struct TreeNode *left;* struct TreeNode *right;* };*/int maxDepth(struct TreeNode* root){…