B004-springcloud alibaba 服务容错 Sentinel

news2024/9/20 18:31:22

目录

      • 高并发带来的问题
      • 服务雪崩效应
      • 常见容错方案
        • 常见的容错思路
          • 隔离
          • 超时
          • 限流
          • 熔断
          • 降级
        • 常见的容错组件
      • Sentinel入门
        • 什么是Sentinel
        • 微服务项目集成Sentinel核心库
        • 安装Sentinel控制台
        • 实现一个接口的限流
      • Sentinel的概念和功能
        • 基本概念
        • 重要功能
      • Sentinel规则
        • 流控规则
          • 三种流控模式
          • 三种内置流控效果
        • 降级规则
        • 热点规则
        • 授权规则
        • 系统规则
        • 自定义异常返回
        • @SentinelResource注解的使用
        • Sentinel规则持久化
      • Feign整合Sentinel

高并发带来的问题

在微服务架构中,我们将业务拆分成一个个的服务,服务与服务之间可以相互调用,但是由于网络原因或者自身的原因,服务并不能保证服务的100%可用,如果单个服务出现问题,调用这个服务就会出现网络延迟,此时若有大量的网络涌入,会形成任务堆积,最终导致服务瘫痪。
在这里插入图片描述
高并发场景模拟见视频

此时会发现,由于order方法囤积了大量请求,导致message方法的访问也出现了问题,这就是服务雪崩的雏形。

服务雪崩效应

在分布式系统中,由于网络原因或自身的原因,服务一般无法保证 100%可用。如果一个服务出现了问题,调用这个服务就会出现线程阻塞的情况,此时若有大量的请求涌入,就会出现多条线程阻塞等待,进而导致服务瘫痪。

由于服务与服务之间的依赖性,故障会传播,会对整个微服务系统造成灾难性的严重后果,这就是服务故障的“雪崩效应”。
在这里插入图片描述
雪崩发生的原因多种多样,有不合理的容量设计,或者是高并发下某一个方法响应变慢,亦或是某台机器的资源耗尽。我们无法完全杜绝雪崩源头的发生,只有做好足够的容错,保证在一个服务发生问题,不会影响到其它服务的正常运行。也就是"雪落而不雪崩"。

常见容错方案

要防止雪崩的扩散,我们就要做好服务的容错,容错说白了就是保护自己不被猪队友拖垮的一些措施,下面介绍常见的服务容错思路和组件。

常见的容错思路

常见的容错思路有隔离、超时、限流、熔断、降级这几种,下面分别介绍一下。

隔离

它是指将系统按照一定的原则划分为若干个服务模块,各个模块之间相对独立,无强依赖。当有故障发生时,能将问题和影响隔离在某个模块内部,而不扩散风险,不波及其它模块,不影响整体的系统服务。常见的隔离方式有:线程池隔离和信号量隔离。
在这里插入图片描述

超时

在上游服务调用下游服务的时候,设置一个最大响应时间,如果超过这个时间,下游未作出反应,就断开请求,释放掉线程。
在这里插入图片描述

限流

限流就是限制系统的输入和输出流量以达到保护系统的目的。为了保证系统的稳固运行,一旦达到需要限制的阈值,就需要限制流量,并采取少量措施以完成限制流量的目的。
在这里插入图片描述

熔断

在互联网系统中,当下游服务因访问压力过大而响应变慢或失败,上游服务为了保护系统整体的可用性,可以暂时切断对下游服务的调用。这种牺牲局部,保全整体的措施就叫做熔断。
在这里插入图片描述
服务熔断一般有三种状态:

熔断关闭状态(Closed)
服务没有故障时,熔断器所处的状态,对调用方的调用不做任何限制

熔断开启状态(Open)
后续对该服务接口的调用不再经过网络,直接执行本地的fallback方法

半熔断状态(Half-Open)尝试恢复服务调用,允许有限的流量调用该服务,并监控调用成功率。如果成功率达到预期,则说明服务已恢复,进入熔断关闭状态;如果成功率仍旧很低,则重新进入熔断关闭状态。

降级

降级其实就是为服务提供一个托底方案,一旦服务无法正常调用,就使用托底方案。
在这里插入图片描述

常见的容错组件

Hystrix
Hystrix 是由Netflix开源的一个延迟和容错库,用于隔离访问远程系统、服务或者第三方库,防止级联失败,从而提升系统的可用性与容错性。

Resilience4J
Resilicence4J 是一款非常轻量、简单,并且文档非常清晰、丰富的熔断工具,这也是Hystrix官方推荐的替代产品。不仅如此,Resilicence4j还原生支持Spring Boot 1.x/2.x,而且监控也支持和prometheus等多款主流产品进行整合。

Sentinel
Sentinel 是阿里巴巴开源的一款断路器实现,本身在阿里内部已经被大规模采用,非常稳定。

下面是三个组件在各方面的对比:
在这里插入图片描述

Sentinel入门

什么是Sentinel

Sentinel (分布式系统的流量防卫兵) 是阿里开源的一套用于服务容错的综合性解决方案。它以流量为切入点,从流量控制、熔断降级、系统负载保护等多个维度来保护服务的稳定性。

Sentinel 具有以下特征:

1.丰富的应用场景:Sentinel 承接了阿里巴巴近 10 年的双十一大促流量的核心场景,例如秒杀(即突发流量控制在系统容量可以承受的范围)、消息削峰填谷、集群流量控制、实时熔断下游不可用应用等。

2.完备的实时监控:Sentinel 提供了实时的监控功能。通过控制台可以看到接入应用的单台机器秒级数据,甚至500 台以下规模的集群的汇总运行情况。

3.广泛的开源生态:Sentinel 提供开箱即用的与其它开源框架/库的整合模块,例如与Spring Cloud、 Dubbo、gRPC 的整合。只需要引入相应的依赖并进行简单的配置即可快速地接入 Sentinel。

4.完善的 SPI 扩展点:Sentinel 提供简单易用、完善的 SPI 扩展接口。您可以通过实现扩展接口来快速地定制逻辑。例如定制规则管理、适配动态数据源等。

Sentinel 分为两个部分:

核心库(Java 客户端) 不依赖任何框架/库/能够运行于所有 Java 运行时环境,同时对 Dubbo / Spring Cloud等框架也有较好的支持。

控制台(Dashboard)基于 Spring Boot 开发,打包后可以直接运行,不需要额外的 Tomcat 等应用容器。

微服务项目集成Sentinel核心库

调用者方/消费者

导包

        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
        </dependency>

编写一个测试controller

@RestController
@Slf4j
public class OrderSentinelController {

    @RequestMapping("/order/message1")
    public String message1(){
        return "message1";
    }

    @RequestMapping("/order/message2")
    public String message2(){
        return "message2";
    }
}
安装Sentinel控制台

Sentinel 提供一个轻量级的控制台,它提供机器发现、单机资源实时监控以及规则管理等功能。

1 下载jar包,https://github.com/alibaba/Sentinel/releases,解压到文件夹

2 cmd启动

# 直接使用jar命令启动项目(控制台本身是一个SpringBoot项目)
java -Dserver.port=8080 -Dcsp.sentinel.dashboard.server=localhost:8080 -Dproject.name=sentinel-dashboard -jar sentinel-dashboard-1.7.0.jar

浏览器访问:http://localhost:8080,默认用户名和密码都是sentinel

3 在服务里添加有关sentinel控制台的配置

spring:
  cloud:
    sentinel:
      transport:
        port: 9999  # Sentinel客户端使用的端口  跟控制台交流的端口,随意指定一个未使用的端口即可
        dashboard: localhost:8080  # Sentinel控制台的地址

4 启动并访问项目:如:http://localhost:8091/order/message1,懒加载:访问后才会记录上去

刷新控制台,即可看到该服务已在里面

补充:了解控制台的使用原理
Sentinel的控制台其实就是一个SpringBoot编写的程序。我们需要将我们的微服务程序注册到控制台上,即在微服务中指定控制台的地址,并且还要开启一个跟控制台传递数据的端口,控制台也可以通过此端口调用微服务中的监控程序获取微服务的各种信息。
在这里插入图片描述

实现一个接口的限流

1.通过控制台为message1添加一个流控规则,最多同时接受两个请求
在这里插入图片描述
浏览器刷新访问测试:
在这里插入图片描述

Sentinel的概念和功能

基本概念

资源
就是Sentinel 要保护的资源
资源是 Sentinel 的关键概念。它可以是 Java 应用程序中的任何内容,可以是一个服务,也可以是一个方法,甚至可以是一段代码。
我们入门案例中的message1方法就可以认为是一个资源

规则
规则就是用来定义如何进行保护资源的
作用在资源之上,定义以什么样的方式保护资源,主要包括流量控制规则、熔断降级规则以及系统保护规则。
我们入门案例中就是为message1资源设置了一种流控规则,限制了进入message1的流量

重要功能

在这里插入图片描述
Sentinel的主要功能就是容错,主要体现为下面这三个:

流量控制

流量控制在网络传输中是一个常用的概念,它用于调整网络包的数据。任意时间到来的请求往往是随机不可控的,而系统的处理能力是有限的。我们需要根据系统的处理能力对流量进行控制。Sentinel作为一个调配器,可以根据需要把随机的请求调整成合适的形状。

熔断降级

当检测到调用链路中某个资源出现不稳定的表现,例如请求响应时间长或异常比例升高的时候,则对这个资源的调用进行限制,让请求快速失败,避免影响到其它的资源而导致级联故障。Sentinel 对这个问题采取了两种手段:

通过并发线程数进行限制
Sentinel通过限制资源并发线程的数量,来减少不稳定资源对其它资源的影响。当某个资源出现不稳定的情况下,例如响应时间变长,对资源的直接影响就是会造成线程数的逐步堆积。当线程数在特定资源上堆积到一定的数量之后,对该资源的新请求就会被拒绝。堆积的线程完成任务后才开始继续接收请求。

通过响应时间对资源进行降级
除了对并发线程数进行控制以外,Sentinel还可以通过响应时间来快速降级不稳定的资源。当依赖的资源出现响应时间过长后,所有对该资源的访问都会被直接拒绝,直到过了指定的时间窗口之后才重新恢复。

Sentinel 和 Hystrix 的区别两者的原则是一致的,都是当一个资源出现问题时,让其快速失败,不要波及到其它服务,但是在限制的手段上确采取了完全不一样的方法:Hystrix采用的是线程池隔离的方式,优点是做到了资源之间的隔离,缺点是增加了钱程切换的成本。Sentinel 采用的是通过并发线程的数量和响应时间来对资源做限制。

系统负载保护
Sentinel同时提供系统维度的自适应保护能力。当系统负载较高的时候,如果还持续让请求进入可能会导致系统崩溃,无法响应。在集群环境下,会把本应这台机器承载的流量转发到其它的机器上去。如果这个时候其它的机器也处在一个边缘状态的时候,Sentinel提供了对应的保护机制,让系统的入口流量和系统的负载达到一个平衡,保证系统在能力范围之内处理最多的请求。

总之一句话:我们需要做的事情,就是在Sentinel的资源上配置各种各样的规则,来实现各种容错的功能。

Sentinel规则

流控规则

流量控制,其原理是监控应用流量的OPS(每秒查询率)或并发线程数等指标,当达到指定的阈值时对流量进行控制,以避免被瞬时的流量高峰冲垮,从而保障应用的高可用性。

点击簇点链路,我们就可以看到访问过的接口地址,然后点击对应的流控按钮,进入流控规则配置页面。新增流控规则界面如下:
在这里插入图片描述
资源名:唯一名称,默认是请求路径,可自定义

针对来源:指定对哪个微服务进行限流,默认指default,意思是不区分来源,全部限制

阈值类型/单机阈值OPS (每秒请求数量):当调用该接口的OPS达到阈值的时候,进行限流线程数:当调用该接口的线程数达到阈值的时候,进行限流

是否集群:暂不需要集群

接下来我们以OPS为例来研究限流规则的配置。

简单配置

我们先做一个简单配置,设置阈值类型为OPS,单机阈值为3。即每秒请求量大于3的时候开始限流。接下来,在流控规则页面就可以看到这个配置。
在这里插入图片描述
然后快速访问/order/message1接口,观察效果。此时发现,当OPS>3的时候,服务就不能正常响应,而是返回Blocked by Sentinel (flow limiting)结果。
在这里插入图片描述

三种流控模式

点击上面设置流控规则的编辑按钮,然后在编辑页面点击高级选项,会看到有流控模式一栏。
在这里插入图片描述
sentinel共有三种流控模式,分别是:
直接(默认):接口达到限流条件时,开启限流
关联:当关联的资源达到限流条件时,开启限流[适合做应用让步]
链路:当从某个接口过来的资源达到限流条件时,开启限流

下面呢分别演示三种模式:

直接流控模式
直接流控模式是最简单的模式,当指定的接口达到限流条件时开启限流。上面案例使用的就是直接流控模式。

关联流控模式
关联流控模式指的是,当指定接口关联的接口达到限流条件时,开启对指定接口开启限流。

第1步:配置限流规则,将流控模式设置为关联,关联资源设置为的/order/message2.
如下:当message2达到每秒超过3个请求时对message1限流
在这里插入图片描述
适合做让步行为
在这里插入图片描述
链路流控模式
链路流控模式指的是,当从某个接口过来的资源达到限流条件时,开启限流。它的功能有点类似于针对来源配置项,区别在于:针对来源是针对上级微服务,而链路流控是针对上级接口,也就是说它的粒度更细。
在这里插入图片描述
环境准备 - 禁止收敛URL的入口 context

从1.6.3 版本开始,Sentinel Web filter默认收敛所有URL的入口context,因此链路限流不生效。

1.7.0 版本开始(对应SCA的2.1.1.RELEASE),官方在CommonFilter 引入了WEB_CONTEXT_UNIFY参数,用于控制是否收敛context。将其配置为false即可根据不同的 URL 进行链路限流。

SCA 2.1.1.RELEASE之后的版本,可以通过配置spring.cloud.sentinel.web-context-unify=false即可关闭收敛

我们当前使用的版本是004-springcloud alibaba 服务容错 Sentinel.1.0.RELEASE,无法实现链路限流。
目前官方还未发布SCA 2.1.2.RELEASE,所以我们只能使用2.1.1.RELEASE,需要写代码的形式实现

(1)暂时将SpringCloud Alibaba的版本调整为2.1.1.RELEASE

<spring-cloud-alibaba.version>2.1.1.RELEASE</spring-cloud-alibaba.version>

(2)配置文件中关闭sentinel的CommonFilter在cloud alibaba里的自动实例化

spring:
	cloud:
		sentinel:
			filter:
				enabled: false

(3)添加一个配置类,自己构建CommonFilter实例

@Configuration
public class FilterContextConfig {

    @Bean
    public FilterRegistrationBean sentinelFilterRegistration() {
        FilterRegistrationBean registration = new FilterRegistrationBean();
        registration.setFilter(new CommonFilter());
        registration.addUrlPatterns("/*");
        // 入口资源关闭聚合
        registration.addInitParameter(CommonFilter.WEB_CONTEXT_UNIFY, "false");
        registration.setName("sentinelFilter");
        registration.setOrder(1);
        return registration;
    }
}

案例

OrderServiceImpl3 - 赋予某方法为资源

@Service
public class OrderServiceImpl3 {

    //定义资源 value指定资源名称  可以在这里改资源名称
    @SentinelResource("message")
    public String message(){
      return "message";
    };
}

OrderSentinelController - 其他资源调用该资源

@RestController
@Slf4j
public class OrderSentinelController {

    @Autowired
    private OrderServiceImpl3 orderServiceImpl3;

    @RequestMapping("/order/message1")
    public String message1(){
        orderServiceImpl3.message();
        return "message1";
    }

    @RequestMapping("/order/message2")
    public String message2(){
        orderServiceImpl3.message();
        return "message2";
    }

}

配置链路流控规则
在这里插入图片描述
浏览器刷新访问:http://localhost:8091/order/message1,会被限制,/order/message2没有影响

三种内置流控效果

流控效果即服务被流控之后以什么效果去处理,有三种内置流控效果
在这里插入图片描述
快速失败
直接抛出异常,异常可修改为自定义显示内容
在这里插入图片描述
Warm Up/预热
它从开始阈值到最大QPS阈值会有一个缓冲阶段,一开始的阈值是最大QPS阈值的1/3,然后慢慢增长,直到最大阈值,适用于将突然增大的流量转换为缓步增长的场景。
排队等待
在这里插入图片描述
例如A到C的QPS是10,限流QPS是5,那么多出的5个请求不会直接失败,会排队等待处理,如果等待时间超过了定义的超时时间,那么就会报失败。

降级规则

降级规则就是设置当满足什么条件的时候,对服务进行降级。Sentinel提供了三个衡量条件:

平均响应时间 :当资源的平均响应时间超过阈值(以 ms 为单位)之后,资源进入准降级状态。如果接下来 1s 内持续进入 5 个请求,它们的 RT都持续超过这个阈值,那么在接下的时间窗口(以 s 为单位)之内,就会对这个方法进行服务降级。

注意 Sentinel 默认统计的 RT 上限是 4900 ms,超出此阈值的都会算作 4900 ms,若需要变更此上限可以通过启动配置项 -Dcsp.sentinel.statistic.max.rt=xxx 来配置。

异常比例:当资源的每秒异常总数占通过量的比值超过阈值之后,资源进入降级状态,即在接下的时间窗口(以 s 为单位)之内,对这个方法的调用都会自动地返回。异常比率的阈值范围是 [0.0, 1.0]。

异常数 :当资源近 1 分钟的异常数目超过阈值之后会进行服务降级。注意由于统计时间窗口是分钟级别的,若时间窗口小于 60s,则结束熔断状态后仍可能再进入熔断状态。

热点规则

热点参数流控规则是一种更细粒度的流控规则, 它允许将规则具体到参数上。
案例
第1步: 编写代码

    @RequestMapping("/order/message3")
    @SentinelResource("message3")//注意这里必须使用这个注解标识,热点规则不生效
    public String message3(String name, Integer age) {
        return name + age;
    }

第2步: 配置热点规则
在这里插入图片描述
第3步: 分别用两个参数访问,会发现只对第一个参数限流了

热点规则增强使用

编辑页面高级选项参数例外项允许对一个参数的具体值进行流控

授权规则

很多时候,我们需要根据调用来源来判断该次请求是否允许放行,这时候可以使用 Sentinel 的来源访问控制的功能。来源访问控制根据资源的请求来源(origin)限制资源是否通过:
若配置白名单,则只有请求来源位于白名单内时才可通过;
若配置黑名单,则请求来源位于黑名单时不通过,其余的请求通过。
在这里插入图片描述
流控应用即来源标识,Sentinel提供了RequestOriginParser 接口来处理来源。只要Sentinel保护的接口资源被访问,Sentinel就会调用RequestOriginParser 的实现类去解析访问来源。

第1步: 自定义来源处理规则

@Component
public class RequestOriginParserDefinition implements RequestOriginParser {
    
    // 定义区分来源:本质作用是通过request域获取到来源标识
    // app,pc
    // 然后交给流控应用位置进行匹配
    @Override
    public String parseOrigin(HttpServletRequest request) {
        String serviceName = request.getParameter("serviceName");
        if (StringUtils.isEmpty(serviceName)){
            throw new RuntimeException("serviceName is empty");
        }
        return serviceName;
    }
}

第2步: 授权规则配置
在这里插入图片描述
第3步: 访问 http://localhost:8091/order/message3?serviceName=pc或其他观察结果

系统规则

系统保护规则是从应用级别的入口流量进行控制,从单台机器的总体 Load、RT、入口 QPS 、CPU使用率和线程数五个维度监控应用数据,让系统尽可能跑在最大吞吐量的同时保证系统整体的稳定性。

系统保护规则是应用整体维度的,而不是资源维度的,并且仅对入口流量 (进入应用的流量) 生效。

Load(仅对 Linux/Unix-like 机器生效):当系统 load1 超过阈值,且系统当前的并发线程数超过系统容量时才会触发系统保护。系统容量由系统的 maxQps * minRt 计算得出。设定参考值一般是 CPU cores * 2.5。

RT:当单台机器上所有入口流量的平均 RT 达到阈值即触发系统保护,单位是毫秒。

线程数:当单台机器上所有入口流量的并发线程数达到阈值即触发系统保护。

入口 QPS:当单台机器上所有入口流量的 QPS 达到阈值即触发系统保护。

CPU使用率:当单台机器上所有入口流量的 CPU使用率达到阈值即触发系统保护。

自定义异常返回
//异常处理页面
@Component
public class ExceptionHandlerPage implements UrlBlockHandler {

    // BlockException  异常接口,包含Sentinel的五个异常
    // FlowException 限流异常
    // DegradeException 降级异常
    // ParamFlowException 参数限流异常
    // AuthorityException 授权异常
    // SystemBlockException 系统负载异常
    @Override
    public void blocked(HttpServletRequest request, HttpServletResponse response, BlockException e) throws IOException {
        response.setContentType("application/json;charset=utf-8");
        ResponseData data = null;

        if (e instanceof FlowException) {
            data = new ResponseData(-1, "接口被限流了...");
        } else if (e instanceof DegradeException) {
            data = new ResponseData(-2, "接口被降级了...");
        }
        response.getWriter().write(JSON.toJSONString(data));
    }
}

@Data
@AllArgsConstructor //全参构造
@NoArgsConstructor //无参构造
class ResponseData {
    private int code;
    private String message;
}
@SentinelResource注解的使用

在定义了资源点之后,我们可以通过Dashboard来设置限流和降级策略来对资源点进行保护。同时还能通过@SentinelResource来指定出现异常时的处理策略。

@SentinelResource用于定义资源,并提供可选的异常处理和 fallback 配置项。其主要参数如下:
在这里插入图片描述
案例一
OrderServiceImpl3

@Slf4j
@Service
public class OrderServiceImpl3 {

    @SentinelResource("message")
    public String message() {
        return "message";
    }

    int i = 0;

    //定义资源 value指定资源名称  可以在这里改资源名称
    //定义当资源内部发生异常的时候的处理逻辑
    // blockHandler 定义当资源内部发生了BlockException应该进入的方法[捕获的是Sentine1定义的异常
    // fallback定义当资源内部发生了 Throwable应该进入的方法
    @SentinelResource(
            value = "message",
            blockHandler = "blockHandler",
            fallback = "fallback"
    )
    public String message(String name) {
        i++;
        if (i % 3 == 0) {
            throw new RuntimeException();
        }
        return "message";
    }

    //blockHandler
    // 要求:
    // 1当前方法的返回值和参数要跟原方法一致
    // 2但是允许在参数列表的最后加入一个参数BlockException,用来接收原方法中发生的异常
    public String blockHandler(String name, BlockException e) {
        //自定义异常处理逻辑
        log.error("触发了BlockException,内容为{}", e);
        return "BlockException";
    }

    //fallback
    // 要求:
    // 1当前方法的返回值和参数要跟原方法一致
    // 2但是允许在参数列表的最后加入一个参数Throwable,用来接收原方法中发生的异常
    public String fallback(String name, Throwable e) {
        //自定义异常处理逻辑
        log.error("触发了Throwable,内容为{}", e);
        return "Throwable";
    }
}

OrderSentinelController

    @RequestMapping("/order/message")
    public String message() {
        return orderServiceImpl3.message("xx");
    }

添加流控规则,浏览器访问测试:http://localhost:8091/order/message,能看到正常返回或两种异常

案例二

OrderServiceImpl3

    //定义资源 value指定资源名称  可以在这里改资源名称
    //定义当资源内部发生异常的时候的处理逻辑
    // blockHandler 定义当资源内部发生了BlockException应该进入的方法[捕获的是Sentine1定义的异常
    // fallback定义当资源内部发生了 Throwable应该进入的方法
    @SentinelResource(
            value = "message",
            blockHandlerClass = OrderServiceImpl3BlockHandler.class,
            blockHandler = "blockHandler",
            fallbackClass = OrderServiceImpl3Fallback.class,
            fallback = "fallback"
    )
    public String message(String name) {
        i++;
        if (i % 3 == 0) {
            throw new RuntimeException();
        }
        return "message";
    }

OrderServiceImpl3BlockHandler

//orderServiceImp13对应的BlockException处理的类
@Slf4j
public class OrderServiceImpl3BlockHandler {
    //blockHandler
    // 要求:
    // 1当前方法的返回值和参数要跟原方法一致
    // 2但是允许在参数列表的最后加入一个参数BlockException,用来接收原方法中发生的异常
    public static String blockHandler(String name, BlockException e) {
        //自定义异常处理逻辑
        log.error("触发了BlockException,内容为{}", e);
        return "BlockException";
    }
}

OrderServiceImpl3Fallback

//orderServiceImp13对应的Throwable处理的类
@Slf4j
public class OrderServiceImpl3Fallback {

    //fallback
    // 要求:
    // 1当前方法的返回值和参数要跟原方法一致
    // 2但是允许在参数列表的最后加入一个参数Throwable,用来接收原方法中发生的异常
    public static String fallback(String name, Throwable e) {
        //自定义异常处理逻辑
        log.error("触发了Throwable,内容为{}", e);
        return "Throwable";
    }
}
Sentinel规则持久化

通过前面的讲解,我们已经知道,可以通过Dashboard来为每个Sentinel客户端设置各种各样的规则,但是这里有一个问题,就是这些规则默认是存放在内存中,极不稳定,所以需要将其持久化。

本地文件数据源会定时轮询文件的变更,读取规则。这样我们既可以在应用本地直接修改文件来更新规则,也可以通过 Sentinel 控制台推送规则。以本地文件数据源为例,推送过程如下图所示:
在这里插入图片描述
首先 Sentinel 控制台通过 API 将规则推送至客户端并更新到内存中,接着注册的写数据源会将新的规则保存到本地的文件中。

1.编写处理类

package cn.ming.config;

import com.alibaba.csp.sentinel.command.handler.ModifyParamFlowRulesCommandHandler;
import com.alibaba.csp.sentinel.datasource.*;
import com.alibaba.csp.sentinel.init.InitFunc;
import com.alibaba.csp.sentinel.slots.block.authority.AuthorityRule;
import com.alibaba.csp.sentinel.slots.block.authority.AuthorityRuleManager;
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRule;
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRuleManager;
import com.alibaba.csp.sentinel.slots.block.flow.FlowRule;
import com.alibaba.csp.sentinel.slots.block.flow.FlowRuleManager;
import com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowRule;
import com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowRuleManager;
import com.alibaba.csp.sentinel.slots.system.SystemRule;
import com.alibaba.csp.sentinel.slots.system.SystemRuleManager;
import com.alibaba.csp.sentinel.transport.util.WritableDataSourceRegistry;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import org.springframework.beans.factory.annotation.Value;
import java.io.File;
import java.io.IOException;
import java.util.List;

//规则持久化
public class FilePersistence implements InitFunc {

    @Value("spring.application:name")
    private String appcationName;

    @Override
    public void init() throws Exception {
        String ruleDir = System.getProperty("user.home") + "/sentinel- rules/" + appcationName;
        String flowRulePath = ruleDir + "/flow-rule.json";
        String degradeRulePath = ruleDir + "/degrade-rule.json";
        String systemRulePath = ruleDir + "/system-rule.json";
        String authorityRulePath = ruleDir + "/authority-rule.json";
        String paramFlowRulePath = ruleDir + "/param-flow-rule.json";

        this.mkdirIfNotExits(ruleDir);
        this.createFileIfNotExits(flowRulePath);
        this.createFileIfNotExits(degradeRulePath);
        this.createFileIfNotExits(systemRulePath);
        this.createFileIfNotExits(authorityRulePath);
        this.createFileIfNotExits(paramFlowRulePath);

        // 流控规则
        ReadableDataSource<String, List<FlowRule>> flowRuleRDS = new FileRefreshableDataSource<>(
                flowRulePath, flowRuleListParser
        );
        FlowRuleManager.register2Property(flowRuleRDS.getProperty());
        WritableDataSource<List<FlowRule>> flowRuleWDS = new
                FileWritableDataSource<>(
                flowRulePath, this::encodeJson
        );
        WritableDataSourceRegistry.registerFlowDataSource(flowRuleWDS);

        // 降级规则
        ReadableDataSource<String, List<DegradeRule>> degradeRuleRDS = new FileRefreshableDataSource<>(
                degradeRulePath, degradeRuleListParser
        );
        DegradeRuleManager.register2Property(degradeRuleRDS.getProperty());
        WritableDataSource<List<DegradeRule>> degradeRuleWDS = new
                FileWritableDataSource<>(
                degradeRulePath, this::encodeJson
        );
        WritableDataSourceRegistry.registerDegradeDataSource(degradeRuleWDS);

        // 系统规则
        ReadableDataSource<String, List<SystemRule>> systemRuleRDS = new FileRefreshableDataSource<>(
                systemRulePath, systemRuleListParser
        );
        SystemRuleManager.register2Property(systemRuleRDS.getProperty());
        WritableDataSource<List<SystemRule>> systemRuleWDS = new
                FileWritableDataSource<>(
                systemRulePath, this::encodeJson
        );
        WritableDataSourceRegistry.registerSystemDataSource(systemRuleWDS);

        // 授权规则
        ReadableDataSource<String, List<AuthorityRule>> authorityRuleRDS = new FileRefreshableDataSource<>(
                authorityRulePath, authorityRuleListParser
        );
        AuthorityRuleManager.register2Property(authorityRuleRDS.getProperty());
        WritableDataSource<List<AuthorityRule>> authorityRuleWDS = new
                FileWritableDataSource<>(
                authorityRulePath, this::encodeJson
        );
        WritableDataSourceRegistry.registerAuthorityDataSource(authorityRuleWDS);

        // 热点参数规则
        ReadableDataSource<String, List<ParamFlowRule>> paramFlowRuleRDS = new FileRefreshableDataSource<>(
                paramFlowRulePath, paramFlowRuleListParser
        );
        ParamFlowRuleManager.register2Property(paramFlowRuleRDS.getProperty());
        WritableDataSource<List<ParamFlowRule>> paramFlowRuleWDS = new
                FileWritableDataSource<>(
                paramFlowRulePath, this::encodeJson
        );
        ModifyParamFlowRulesCommandHandler.setWritableDataSource(paramFlowRuleWDS);
    }

    private Converter<String, List<FlowRule>> flowRuleListParser = source -> JSON.parseObject(
            source,
            new TypeReference<List<FlowRule>>() {
            }
    );
    private Converter<String, List<DegradeRule>> degradeRuleListParser = source
            -> JSON.parseObject(
            source,
            new TypeReference<List<DegradeRule>>() {
            }
    );
    private Converter<String, List<SystemRule>> systemRuleListParser = source -> JSON.parseObject(
            source, new TypeReference<List<SystemRule>>() {
            }
    );
    private Converter<String, List<AuthorityRule>> authorityRuleListParser = source -> JSON.parseObject(
            source,
            new TypeReference<List<AuthorityRule>>() {
            }
    );
    private Converter<String, List<ParamFlowRule>> paramFlowRuleListParser = source -> JSON.parseObject(
            source,
            new TypeReference<List<ParamFlowRule>>() {
            }
    );

    private void mkdirIfNotExits(String filePath) throws IOException {
        File file = new File(filePath);
        if (!file.exists()) {
            file.mkdirs();
        }
    }
    private void createFileIfNotExits(String filePath) throws IOException {
        File file = new File(filePath);
        if (!file.exists()) {
            file.createNewFile();
        }
    }

    private <T> String encodeJson(T t) {
        return JSON.toJSONString(t);
    }
}

2.添加配置

在resources下创建配置目录 META-INF/services,然后添加文件com.alibaba.csp.sentinel.init.InitFunc
在文件中添加配置类的全路径

cn.ming.config.FilePersistence

Feign整合Sentinel

第1步: 引入sentinel的依赖

<!--sentinel客户端-->
<dependency>
	<groupId>com.alibaba.cloud</groupId>
	<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>

第2步: 在配置文件中开启Feign对Sentinel的支持

feign:
	sentinel: 
		enabled: true

第3步: 创建容错类

//这是一个容错类,需要实现Fegin所在的接口,并去实现接口中的所有方法
// 一旦Fegin远程调用出现问题了,就会进入当前类中同名方法,执行容错逻辑
@Component
public class ProductClientFallback implements ProductClient {

    @Override
    public Product getProductByPid(Integer pid) {
        //容错逻辑
        Product product = new Product();
        product.setPid(-100);
        product.setPname("远程调用商品微服务出现异常了,进入了容错逻辑");
        return product;
    }
}

第4步: 通过fallback属性为被调用的接口指定容错类

// value用于指定调用nacos下哪个微服务
// fallback用于指定当前fegin接口的容错类
@FeignClient(value = "service-product", fallback = ProductClientFallback.class)
public interface ProductClient {

    // 商品信息查询
    @RequestMapping("/product/{pid}")   //@FeignClient的value + @RequestMapping的value值 其实就是完整的请求地址
    Product getProductByPid(@PathVariable("pid") Integer pid);
}

第5步: 改造OrderController

    /**
     * 用nacos调用服务,并用feign实现带有负载均衡的调用
     */
    @RequestMapping("/order/product5/{pid}")
    public Order createOrder5(@PathVariable("pid") Integer pid) {
        log.info("接收到{}号商品的下单请求,接下来调用商品微服务查询此商品信息", pid);
        // 调用商品微服务,查询商品信息
        Product product = productClient.getProductByPid(pid);

        if(product.getPid() == -100){	//代表走到了容错逻辑
            Order order = new Order();
            order.setOid(-100l);
            order.setPname("下单失败");
            return order;
        }

        log.info("查询到{}号商品的信息,内容是{}", pid, JSON.toJSONString(product));

        // 下单/创建订单
        Order order = new Order();
        order.setUid(1);
        order.setUsername("测试用户");
        order.setPid(product.getPid());
        order.setPname(product.getPname());
        order.setPprice(product.getPprice());
        order.setNumber(1);
        orderService.createOrder(order);
        log.info("创建订单成功,订单信息为{}", JSON.toJSONString(order));
        return order;
    }

第6步: 启动order和product两个微服务,下单正常,关闭product服务,下单会走入容错逻辑

另一种方式:实现FallbackFactory容错,可获取到调用异常

编写类ProductClientFallbackFactory,获取到调用异常

//这是容错类,他要求我们实现一个FallbackFactory<要为哪个接口产生容错类>
@Slf4j
@Component //或者@Service也可以
public class ProductClientFallbackFactory implements FallbackFactory<ProductClient> {

    //Throwable  这就是fegin在调用过程中产生的异常
    @Override
    public ProductClient create(Throwable throwable) {

        return new ProductClient() {
            @Override
            public Product getProductByPid(Integer pid) {
                log.error("throwable1: {}",throwable);
                log.error("throwable2: {}", JSON.toJSONString(throwable));
                Product product = new Product();
                product.setPid(-100);
                product.setPname("远程调用商品微服务出现异常了,进入了容错逻辑");
                return product;
            }
        };
    }
}

改造ProductClient,注释fallback属性,增加fallbackFactory属性

// value用于指定调用nacos下哪个微服务
// fallback用于指定当前fegin接口的容错类
@FeignClient(
        value = "service-product",
//        fallback = ProductClientFallback.class,
        fallbackFactory = ProductClientFallbackFactory.class
)
public interface ProductClient {

    // 商品信息查询
    @RequestMapping("/product/{pid}")   //@FeignClient的value + @RequestMapping的value值 其实就是完整的请求地址
    Product getProductByPid(@PathVariable("pid") Integer pid);
}

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

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

相关文章

拒绝云测,热门猫主食冻干对比测评,希喂、SC、VE谁实力更强?

在当今的科学养宠时代&#xff0c;主食冻干已经成为了猫日常饮食不可或缺的一部分。高肉含量的主食冻干不仅易吸收、好消化&#xff0c;更能给猫提供其他猫粮所不能提供的微量物质&#xff0c;更满足猫的全面营养需求。然而&#xff0c;在众多品牌和口味的主食冻干中&#xff0…

抖音找人推广要给多少推广费?CloudNEO:9000+网红资源,助您品牌代言

抖音作为中国最受欢迎的短视频平台之一&#xff0c;吸引了众多用户的关注&#xff0c;也成为了企业推广的热门渠道。然而&#xff0c;很多人对于在抖音上找人推广需要支付多少推广费并不了解。下面让我们来解析一下抖音推广费用的计算方式。 1. 推广形式&#xff1a; 首先&…

ISP技术综述

原文来自技术前沿&#xff1a;ISP芯片终极进化——VP芯片&#xff08;AI视觉处理器&#xff09; 目录 1.计算机视觉的定义 2.与计算机视觉密切相关的概念与计算机视觉密切相关的概念有机器视觉&#xff0c;图像处理与分析&#xff0c;图像和视频理解。 3.计算机视觉的应用 …

[自研开源] MyData v0.7.3 更新日志

开源地址&#xff1a;gitee | github 详细介绍&#xff1a;MyData 基于 Web API 的数据集成平台 部署文档&#xff1a;用 Docker 部署 MyData 使用手册&#xff1a;MyData 使用手册 试用体验&#xff1a;https://demo.mydata.work 交流Q群&#xff1a;430089673 介绍 MyData …

git常见使用

1. 概念 分布式&#xff0c;有远程仓库和本地仓库的概念&#xff0c;因此要注意同步问题git是面向对象的&#xff0c;本质是内容寻址系统。.git目录下有个文件夹objects&#xff0c;存储git库中的对象&#xff0c;git就是根据object建立一种树形结构&#xff0c;将文件和通过h…

数据库应用:Linux 部署 GaussDB

目录 一、实验 1.环境 2.Linux 部署 GaussDB 3.Linux 使用 GaussDB 4.使用 GaussDB 进行表与索引操作 5.使用 GaussDB 进行视图操作 6.使用 GaussDB 进行联表查询 7.使用 GaussDB 进行外键关联 二、问题 1.运行python脚本报错 2. 安装GaussDB 报错 3. install 安装…

广州大彩科技新品发布:大彩科技COF系列2.4寸串口屏发布!

一、产品介绍 此次发布的是S系列平台2.4寸COF超薄结构串口屏&#xff0c;分辨率为240*320&#xff0c;该平台采用了Cortex-M3内核的处理器&#xff0c;内置了2Mbyte PSRAM和64Mbit FLASH&#xff0c;是专为小尺寸串口屏设计的MCU&#xff0c;精简了外围电路。 该平台默认支持大…

【sql】深入理解 mysql的EXISTS 语法

相关文章&#xff1a; 【sql】深入理解 mysql的EXISTS 语法 【sql】初识 where EXISTS 1. 使用格式如下&#xff1a; select * from a where exists ( 任何子查询 ) 代码根据颜色分成两段&#xff0c;前面的是主查询&#xff0c;后面红色的是子查询&#xff0c;先主后子&…

从政府工作报告探计算机行业发展

文章目录 每日一句正能量前言以“数”谋新、加“数”向实人工智能方面人工智能成核心驱动引擎 软件方面通信方面后记 每日一句正能量 该来的始终会来&#xff0c;千万别太着急&#xff0c;如果你失去了耐心&#xff0c;就会失去更多。该走过的路总是要走过的&#xff0c;从来不…

Android14之HIDL报错:Invalid sparse file format at header magic(一百九十六)

简介&#xff1a; CSDN博客专家&#xff0c;专注Android/Linux系统&#xff0c;分享多mic语音方案、音视频、编解码等技术&#xff0c;与大家一起成长&#xff01; 优质专栏&#xff1a;Audio工程师进阶系列【原创干货持续更新中……】&#x1f680; 优质专栏&#xff1a;多媒…

【数据结构与算法】(17):计数排序和基数排序详解

&#x1f921;博客主页&#xff1a;Code_文晓 &#x1f970;本文专栏&#xff1a;数据结构与算法 &#x1f63b;欢迎关注&#xff1a;感谢大家的点赞评论关注&#xff0c;祝您学有所成&#xff01; ✨✨&#x1f49c;&#x1f49b;想要学习更多数据结构与算法点击专栏链接查看&…

智慧城市与数字孪生:科技融合助力城市可持续发展

随着信息技术的迅猛发展&#xff0c;智慧城市和数字孪生作为现代城市发展的重要理念和技术手段&#xff0c;正日益受到广泛关注。智慧城市通过集成应用先进的信息通信技术&#xff0c;实现城市管理、服务、运行的智能化&#xff0c;而数字孪生则是利用数字化手段对物理城市进行…

面试笔记——Redis(使用场景、面临问题、缓存穿透)

Redis的使用场景 Redis&#xff08;Remote Dictionary Server&#xff09;是一个内存数据结构存储系统&#xff0c;它以快速、高效的特性闻名&#xff0c;并且它支持多种数据结构&#xff0c;包括字符串、哈希表、列表、集合、有序集合等。它主要用于以下场景&#xff1a; 缓…

libmodbus编译为64位动态库

通用方法&#xff0c;记录一下&#xff0c;以便后续参考。 Step 1. 下载libmodbus源码 GitHub - stephane/libmodbus: A Modbus library for Linux, Mac OS, FreeBSD and Windows Step 2. 生成配置文件 进入libmodbus-master\src\win32目录&#xff0c;在该目录下打开终端&am…

【火猫TV】DOTA2 BB队员称:队伍非常具有凝聚力

1、近日BB战队队员Nightfall接受采访时表示战队自从去年获得梦幻联赛S20亚军以来&#xff0c;就非常具有凝聚力。 “我认为战队自从去年获得梦幻联赛S20亚军以来&#xff0c;战队就非常具有凝聚力。从那一刻开始&#xff0c;我们这群人不再只是几个选手组建起来的松散组织&…

移动云COCA架构实现算力跃升,探索人工智能新未来

近期&#xff0c;随着OpenAI正式发布首款文生视频模型Sora&#xff0c;标志着人工智能大模型在视频生成领域有了重大飞跃。Sora模型不仅能够生成逼真的视频内容&#xff0c;还能够模拟物理世界中的物体运动与交互&#xff0c;其核心在于其能够处理和生成具有复杂动态与空间关系…

C++的语法

可能需要用到存储各种数据类型&#xff08;比如字符型、宽字符型、整型、浮点型、双浮点型、布尔型等&#xff09; 下表显示了各种变量类型在内存中存储值时需要占用的内存&#xff0c;以及该类型的变量所能存储的最大值和最小值。 注意&#xff1a;不同系统会有所差异 #inc…

8.发布页面

发布页面 官网 https://vkuviewdoc.fsq.pub/components/form.html 复制官网中的内容 代码 write.vue <template><view class"u-wrap u-p-l-20 u-p-r-20"><u-form :model"addModel" ref"form1"><u-form-item label&quo…

出彩不停息!创维汽车SKYHOME又获国际大奖

祝贺&#xff01;创维汽车SKYHOME又获国际缪斯设计大奖&#xff01;进一步获得国际认可&#xff01; 卓越的意识、优秀的审美、无与伦比的专注&#xff0c;不仅是缪斯奖所看重的独特品质&#xff0c;也是SKYHOME设计团队在传递品牌故事中所秉持的优秀品格。作为缪斯奖青睐的设计…

干货分享 | 外贸搞钱必备!

一、谷歌搜索&#xff1a; 开发客户的神器&#xff0c;需结合搜索指令&#xff0c;常见的12大搜索指令如下&#xff1a; 1. 指令运用&#xff1a;email 说明&#xff1a;指定搜索内容&#xff0c;缩小范围&#xff0c;显示带有email的搜索结果 2.- 指令运用&#xff1a;-B2C 说…