【SpringCloud Alibaba笔记】(2)Sentinel实现熔断与限流

news2024/9/24 5:29:25

Sentinel

概述

官网:https://github.com/alibaba/Sentinel
中文文档:https://sentinelguard.io/zh-cn/docs/introduction.html

类似Hystrix,以流量为切入点,从流量路由、流量控制、流量整形、熔断降级、系统自适应过载保护、热点流量防护等多个维度来帮助开发者保障微服务的稳定性

Hystrix与Sentinel区别

  • 需要我们程序员自己手工搭建监控平台
    没有一套web界面可以给我们进行更加细粒度化得配置流控、速率控制、服务熔断、服务降级…
  • 单独一个组件,可以独立出来。
    直接界面化的细粒度统一配置。

下载与安装

https://github.com/alibaba/Sentinel/releases

下载的是一个jar包

运行前提:8080端口不被占用,有java8环境

启动命令:java -jar sentinel-dashboard-1.7.0.jar

启动完成后访问http://localhost:8080/#/login,账号密码均为sentinel

在这里插入图片描述

Sentinel特性
在这里插入图片描述
Sentinel 分为两个部分:

  • 核心库(Java 客户端)不依赖任何框架/库,能够运行于所有 Java 运行时环境,同时对 Dubbo / Spring Cloud 等框架也有较好的支持。
  • 控制台(Dashboard)基于 Spring Boot 开发,打包后可以直接运行,不需要额外的 Tomcat 等应用容器。

初始化演示工程

先启动nacos

新建cloudalibaba-sentinel-service8401模块

pom文件

	<artifactId>cloudalibaba-sentinel-service8401</artifactId>
    <dependencies>
        <dependency><!-- 引入自己定义的api通用包,可以使用Payment支付Entity -->
            <groupId>com.mzr.springcloud</groupId>
            <artifactId>cloud-api-commons</artifactId>
            <version>${project.version}</version>
        </dependency>
        <!--SpringCloud ailibaba nacos -->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>
        <!--SpringCloud ailibaba sentinel -->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
        </dependency>
        <!--openfeign-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>
        <!-- SpringBoot整合Web组件+actuator -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
    </dependencies>

yml

server:
  port: 8401

spring:
  application:
    name: cloudalibaba-sentinel-service
  cloud:
    nacos:
      discovery:
        server-addr: 192.168.254.130:80 #Nacos服务注册中心地址,使用集群地址(可以看我上一章关于nacos笔记)
    sentinel:
      transport:
        dashboard: localhost:8080 #配置Sentinel dashboard地址
        #默认8719端口,假如被占用会自动从8719开始依次+1扫描,直至找到未被占用的端口
        port: 8719

management:
  endpoints:
    web:
      exposure:
        include: '*'

主启动类

@EnableDiscoveryClient
@SpringBootApplication
public class MainApp8401
{
    public static void main(String[] args) {
        SpringApplication.run(MainApp8401.class, args);
    }
}

controller

@RestController
@Slf4j
public class FlowLimitController
{
    @GetMapping("/testA")
    public String testA()
    {
        return "------testA";
    }

    @GetMapping("/testB")
    public String testB()
    {
        log.info(Thread.currentThread().getName()+"\t"+"...testB");
        return "------testB";
    }
}

启动微服务

访问http://localhost:8080/ 我们发现sentinel里空空如也,这是为什么呢?

在这里插入图片描述

因为sentinel使用的是懒加载机制,我们只需执行一次访问http://localhost:8401/testA即可

此时,sentinel正在监控微服务8401

在这里插入图片描述

流控规则

在这里插入图片描述

  • 资源名:唯一名称,默认请求路径

  • 针对来源:Sentinel可以针对调用者进行限流,填写微服务名,默认default(不区分来源)

  • 阈值类型单机阈值:

    QPS(每秒钟的请求数量):当调用该api的QPS达到阈值的时候,进行限流。

    线程数:当调用该api的线程数达到阈值的时候,进行限流

  • 是否集群:不需要集群

  • 流控模式

    直接:api达到限流条件时,直接限流

    关联:当关联的资源达到阈值时,就限流自己

    链路:只记录指定链路上的流量(指定资源从入口资源进来的流量,如果达到阈值就进行限流)【api级别的针对来源】

  • 流控效果

    快速失败:直接失败,抛异常

    Warm up:根据codeFactor (冷加载因子,默认3)的值,从阈值/codeFactor,经过预热时长,才达到设置的QPS阈值

    排队等待:匀速排队,让请求以匀速的速度通过,阈值类型必须设置为QPS,否则无效

流程模式

直接模式(默认)

演示:表示/请求路径testA 1秒钟内查询1次就是OK,若超过次数1,就直接快速失败,报默认错误

在这里插入图片描述

当我们快速连点时,就会报错

在这里插入图片描述
线程数与QPS大致相同,设置为1时表示在一个线程正在处理时,有其他线程进来会返回Blocked by Sentienl(flow limiting),之后全部使用QPS演示

直接调用默认报错信息,技术方面OK,but是否应该有我们自己的后续处理(我们自定义报错信息)?

关联模式

当与A关联的资源B达到阀值后,就限流A自己(支付微服务达到阈值,订单模块限流,避免连坐)

演示:设置/请求路径testB QPS为1,模拟并发密集访问/testB一定会导致B达到阈值,观察A的情况

在这里插入图片描述

postman模拟并发密集访问/testB(20个线程间隔0.3s访问一次)

在这里插入图片描述

在模拟期间我们访问 http://localhost:8401/testA 会发现A挂了,这就是关联模式

在这里插入图片描述

链路模式

只针对从指定链路访问到本资源的请求做统计,判断是否超过阈值。

演示:有两条链路 /testA -> /common、/testB -> /common

如果从 /testA 进入到 /common的请求阈值超过了1次/s,则对/testA 进行限流

在这里插入图片描述

流控效果

直接效果->快速失败(默认的流控处理)

直接失败,抛出异常Blocked by Sentinel (flow limiting)

源码:com.alibaba.csp.sentinel.slots.block.flow.controller.DefaultController

预热效果

秒杀系统在开启的瞬间,会有很多流量上来,很有可能把系统打死,预热方式就是把为了保护系统,可慢慢的把流量放进来,慢慢的把阀值增长到设置的阀值。

Warm up ( RuleConstant.CONTROL_BEHAVIOR_MARM_UP)方式,即预热/冷启动方式。当系统长期处于低水位的情况下,当流量突然增加时,直接把系统拉升到高水位可能瞬间把系统压垮。通过"冷启动",让通过的流量缓慢增加,在一定时间内逐渐增加到阈值上限,给冷系统一个预热的时间,避免冷系统被压垮。详细文档可以参考流量控制- Warm Up文档,具体的例子可以参见WarmUpFlowDemo。

通常冷启动的过程系统允许通过的QPS曲线如下图所示:

在这里插入图片描述

源码所在类:com.alibaba.csp.sentinel.slots.block.flow.controller.WarmUpController

默认coldFactlor为3,即请求QPS 从threshold / 3开始,经预热时长逐渐升至设定的QPS阈值

演示:阀值为10+预热时长设置5秒。
系统初始化的阀值为10/3约等于3,即阀值刚开始为3,然后过了5秒后阀值才慢慢升高恢复到10

在这里插入图片描述

排队等待效果

匀速排队(RuleConstant.CONTROL_BEHAVIOR_RATE_LINITER)方式会严格控制请求通过的间隔时间,也即是让请求以均匀的速度通过,对应的是漏桶算法。详细文档可以参考流量控制-匀速器模式,具体的例子可以参见PaceFlowDemo。

该方式的作用如下图所示:
在这里插入图片描述
这种方式主要用于处理间隔性突发的流量,例如消息队列。
想象一下这样的场景,在某一秒有大量的请求到来,而接下来的几秒则处于空闲状态,我们希望系统能够在接下来的空闲期间逐渐处理这些请求,而不是在第一秒直接拒绝多余的请求。

匀速排队,让请求以均匀的速度通过,阀值类型必须设成QPS,否则无效。

演示:/testA每秒1次请求,超过的话就排队等待,等待的超时时间为20000毫秒。

在这里插入图片描述

降级规则

官网

在这里插入图片描述

  • RT(平均响应时间,秒级)

当1s内持续进入5个请求即每秒请求量QPS>=5对应时刻的平均响应时间(秒级)均超过阈值 count,(以ms为单位),那么在接下的时间窗口( DegradeRule中的timewindow,以s为单位)之内,对这个方法的调用都会自动地熔断(抛出DegradeException)。

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

  • 异常比列(秒级)

当资源的每秒请求量QPS>=5,并且每秒异常总数占通过量的比值超过阈值( DegradeRule中的count)之后,资源进入降级状态,即在接下的时间窗口( DegradeRule 中的timeWindow,以s为单位)之内,对这个方法的调用都会自动地返回。

异常比率的阈值范围是[0.0,1.0],代表0% -100%。

  • 异常数(分钟级)

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

Sentinel的断路器是没有半开状态的

半开的状态系统自动去检测是否请求有异常,没有异常就关闭断路器恢复使用,
有异常则继续打开断路器不可用。具体可以参考Hystrix

RT规则

@GetMapping("/testD")
    public String testD()
    {
        try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); }
        log.info("testD 测试RT");
        return "------testD";
    }

在这里插入图片描述

在这里插入图片描述

永远一秒钟打进来10个线程(大于5个了)调用testD,我们希望200毫秒(程序设计为1s)处理完本次任务,如果超过200毫秒还没处理完,在未来1秒钟的时间窗口内,断路器打开(保险丝跳闸)微服务不可用,保险丝跳闸断电了

在这里插入图片描述

后续我停止jmeter,没有这么大的访问量了,断路器关闭(保险丝恢复),微服务恢复OK

在这里插入图片描述

异常比例规则

@GetMapping("/testD")
    public String testD()
    {
        log.info("testD 异常比例");
        int age = 10/0;
        return "------testD";
    }

在这里插入图片描述
在这里插入图片描述
启动测试,访问http://localhost:8401/testD

单独访问一次,必然来一次报错一次(int age = 10/0),调一次错一次
在这里插入图片描述

开启jmeter后,直接高并发发送请求,多次调用达到我们的配置条件(异常比例0.2)

断路器开启(保险丝跳闸),微服务不可用了,再次访问不再报错error而是服务降级了。

在这里插入图片描述

异常数规则

@GetMapping("/testE")
    public String testE()
    {
        log.info("testE 异常数");
        int age = 10/0;
        return "------testE";
    }

在这里插入图片描述
启动测试,访问http://localhost:8401/testE

第一次访问绝对报错(int age = 10/0),因为除数不能为零我们看到error窗口

在这里插入图片描述

但是达到5次报错后,进入熔断后降级

在这里插入图片描述

热点key限流

在这里插入图片描述

源码类:com.alibaba.csp.sentinel.slots.block.BlockException

何为热点?热点即经常访问的数据。
很多时候我们希望统计某个热点数据中访问频次最高的 Top K 数据,并对其访问进行限制。比如:

  • 商品 ID 为参数,统计一段时间内最常购买的商品 ID 并进行限制
  • 用户 ID 为参数,针对一段时间内频繁访问的用户 ID 进行限制

热点参数限流会统计传入参数中的热点参数,并根据配置的限流阈值与模式,对包含热点参数的资源调用进行限流。热点参数限流可以看做是一种特殊的流量控制,仅对包含热点参数的资源调用生效。

兜底方法 分为系统默认和客户自定义两种
之前的case,限流出问题后,都是用sentinel系统默认的提示:Blocked bySentinel (flow limiting)
我们能不能自定义呢?
类似hystrix,某个方法出问题了,就找对应的兜底降级方法?
@SentinelResource可以帮我们实现,这里先引入使用以下,之后会详细讲解
这里声明一下:@SentinelResource与热点规则没有必然联系,这里使用@SentinelResource只是为了演示自定义返回限流提示

在8401模块controller添加方法

	@GetMapping("/testHotKey")
	//@SentinelResource的value值随意,唯一即可,一般为了编码统一规范与请求地址保持一致
	//blockHandler = "deal_testHotKey" 如果违背了Sentinel配置规则,则调用deal_testHotKey方法
    @SentinelResource(value = "testHotKey",blockHandler = "deal_testHotKey")
    public String testHotKey(@RequestParam(value = "p1",required = false) String p1,
                             @RequestParam(value = "p2",required = false) String p2)
    {
        //int age = 10/0;
        return "------testHotKey";
    }
    //方法头基本与@SentinelResource配置方法一致,只是入参必须要加BlockException exception 
    public String deal_testHotKey (String p1, String p2, BlockException exception)
    {
        return "------deal_testHotKey,o(╥﹏╥)o";  //sentinel系统默认的提示:Blocked by Sentinel (flow limiting)
    }

演示:方法testHotKey里面第一个参数只要QPS超过每秒1次,马上降级处理,并返回自定义结果

资源名:这里可以传@SentinelResource的value

参数索引:传入的热点参数的索引(从0开始)

在这里插入图片描述

启动测试

一秒访问一次 http://localhost:8401/testHotKey?p1=0 返回值正常,当我们快速访问两次会发现返回o(╥﹏╥)o,发现服务已经降级

在这里插入图片描述
此时我们去掉@SentinelResource中的blockHandler = “deal_testHotKey”,再次访问

异常打到了前台用户界面看到,不友好,所以建议一定要配置上blockHandler

在这里插入图片描述

上述案例演示了第一个参数p1,当QPS超过1秒1次点击后马上被限流

偶尔也会有特殊情况,比如我们希望参数p1值为5时,它的阈值可以达到200,值为1时阈值为依然为1

这时我们应该如何配置呢?这里需要用到高级配置,如下

整体配置含义:参数下标是第0个也就是p1时,当QPS超过1秒1次点击后马上被限流,当p1值为5时,阈值则变为200,其他值阈值都为1

在这里插入图片描述

启动测试

p1=1时,我们快速连续访问两次便会触发限流

在这里插入图片描述
当p1=5时,我们手动快速点击多次(不超过200)都正常返回,达到我们的预期

在这里插入图片描述
此时,我们突然手贱,在controller层添加了程序异常(int age = 10/0),如下

	@GetMapping("/testHotKey")
    @SentinelResource(value = "testHotKey",blockHandler = "deal_testHotKey")
    public String testHotKey(@RequestParam(value = "p1",required = false) String p1,
                             @RequestParam(value = "p2",required = false) String p2)
    {
        int age = 10/0;
        return "------testHotKey";
    }

启动测试

发现异常打到了前台用户界面,怎么回事??

在这里插入图片描述

SentinelResource处理的是sentinel控制台配置的违规情况,有blockHandler方法配置的兜底处理

RuntimeException是java运行时报出的,运行时异常RunTimeException,@SentinelResource不管

总结:sentinelResource主管配置出错,运行出错该走异常走异常

系统规则

官网

我们上边学习的限流都是方法级别的,只对某个方法有效,如果我们想对整个系统进行限流

便需要使用系统规则,从整体维度对应用入口流量进行控制

在这里插入图片描述
系统规则支持以下的模式:

  • Load自适应(仅对Linux/Unix-like机器生效):系统的load1作为启发指标,进行自适应系统保护。当系统load1超过设定的启发值,且系统当前的并发线程数超过估算的系统容量时才会触发系统保护(BBR阶段)。系统容量由系统的 maxQps * minRt 估算得出。设定参考值一般是 CPU cores * 2.5 。
  • CPU usage (1.5.0+版本)︰当系统CPU使用率超过阈值即触发系统保护(取值范围0.0-1.0),比较灵敏。
  • 平均RT:当单台机器上所有入口流量的平均RT达到阈值即触发系统保护,单位是毫秒。
  • 并发线程数:当单台机器上所有入口流量的并发线程数达到阈值即触发系统保护。
  • 入口QPS:当单台机器上所有入口流量的QPS达到阈值即触发系统保护。

系统规则使用的不多,颗粒度太大

使用入口QPS演示

当QPS超过1秒1次时,触发系统服务限流

在这里插入图片描述

启动测试

我们分别单点访问/testA、/testB方法是正常返回结果的

当我们快速连续访问/testA、/testB,AB均已限流

得出结论:我们配置的系统规则对所有方法都有效

在这里插入图片描述
在这里插入图片描述

@SentinelResource详解

按资源名称限流+后续处理

改造8401微服务

新增RateLimitController

@SentinelResource包含blockHandler 属性

@RestController
public class RateLimitController
{
    @GetMapping("/byResource")
    @SentinelResource(value = "byResource",blockHandler = "handleException")
    public CommonResult byResource()
    {
        return new CommonResult(200,"按资源名称限流测试OK",new Payment(2020L,"serial001"));
    }
    public CommonResult handleException(BlockException exception)
    {
        return new CommonResult(444,exception.getClass().getCanonicalName()+"\t 服务不可用");
    }
}

配置流控规则

在这里插入图片描述

启动测试,访问http://localhost:8401/byResource,一秒一次正常返回

在这里插入图片描述

快速多次访问,返回了自己定义的限流处理信息,限流发生

在这里插入图片描述

@SentinelResource不包含blockHandler 属性

如果我们此时去掉@SentinelResource(value = “byResource”,blockHandler = “handleException”)中的blockHandler

再次快速多次访问 http://localhost:8401/byResource,会发现出现了error页面 很不友好对吧!!这个我们在学习热点限流时已经接触过了

在这里插入图片描述

此时重新启动微服务8401看看

Sentinel控制台,流控规则消失了? ? ? ?

在这里插入图片描述

这个问题后续会讲解

按照Url地址限流+后续处理

继续使用该方法

@RestController
public class RateLimitController
{
    @GetMapping("/byResource")
    @SentinelResource(value = "byResource",blockHandler = "handleException")
    public CommonResult byResource()
    {
        return new CommonResult(200,"按资源名称限流测试OK",new Payment(2020L,"serial001"));
    }
    public CommonResult handleException(BlockException exception)
    {
        return new CommonResult(444,exception.getClass().getCanonicalName()+"\t 服务不可用");
    }
}

配置流控规则

在这里插入图片描述

启动测试,访问一次 http://localhost:8401/byResource

在这里插入图片描述

快速多次访问,会发现返回默认信息而不是我们自定义的信息

在这里插入图片描述

上面两种配置的区别及兜底方案面临的问题

区别

按url限流: 配置资源名为访问路径,使用url限流时触发流控规则会报默认错误。

按资源限流: 如果@SentinelResource
指定了blockHandler 会执行自定义的方法 如上handleException()
没指定blockHandler 会将异常打到了前台用户界面看到,很不友好!!
实际开发中超过阈值时,我们要使用@SentinelResource注释一定要定义一些兜底的方法。

面临的问题

系统默认的,没有体现我们自己的业务要求。

依照现有条件,我们自定义的处理方法又和业务代码耦合在一块,不直观。

每个业务方法都添加一个兜底的,代码膨胀加剧。

全局统—的处理方法没有体现。

客户自定义限流处理逻辑

创建CustomerBlockHandler类用于自定义限流处理逻辑

创建自定义限流处理类com.mzr.springcloud.CustomerBlockHandler

public class CustomerBlockHandler
{
    public static CommonResult handlerException(BlockException exception)
    {
        return new CommonResult(4444,"按客戶自定义,global handlerException----1");
    }
    public static CommonResult handlerException2(BlockException exception)
    {
        return new CommonResult(4444,"按客戶自定义,global handlerException----2");
    }
}

RateLimitController

@GetMapping("/rateLimit/customerBlockHandler")
    @SentinelResource(value = "customerBlockHandler",
    		//限流时分门别类的选择调用哪个类中的哪个异常方法
    		//例如CustomerBlockHandler中的handlerException2方法
            blockHandlerClass = CustomerBlockHandler.class,
            blockHandler = "handlerException2")
    public CommonResult customerBlockHandler() {
        return new CommonResult(200, "按客戶自定义", new Payment(2020L, "serial003"));
    }

配置流控规则

在这里插入图片描述

启动测试,访问一次 http://localhost:8401/rateLimit/customerBlockHandler

在这里插入图片描述

多次快速访问,可以看到返回的是我们自定义的方法

在这里插入图片描述

测试后我们自定义的出来了进一步说明

在这里插入图片描述

@SentinelResource更多注解属性说明

在这里插入图片描述

服务熔断功能

sentinel整合ribbon+openFeign+fallback

Ribbon环境

新建cloudalibaba-provider-payment9003/9004

9003/9004配置基本相同,记得修改端口号

pom

<dependencies>
        <!--SpringCloud ailibaba nacos -->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>
        <dependency><!-- 引入自己定义的api通用包,可以使用Payment支付Entity -->
            <groupId>com.mzr.springcloud</groupId>
            <artifactId>cloud-api-commons</artifactId>
            <version>${project.version}</version>
        </dependency>
        <!-- SpringBoot整合Web组件 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
    </dependencies>

yml

server:
  port: 9003

spring:
  application:
    name: nacos-payment-provider
  cloud:
    nacos:
      discovery:
        server-addr: 192.168.254.138:8848 #配置Nacos地址

management:
  endpoints:
    web:
      exposure:
        include: '*'

主启动类

@SpringBootApplication
@EnableDiscoveryClient
public class PaymentMain9003
{
    public static void main(String[] args) {
        SpringApplication.run(PaymentMain9003.class, args);
    }
}

controller

@RestController
public class PaymentController
{
    @Value("${server.port}")
    private String serverPort;

    public static HashMap<Long, Payment> hashMap = new HashMap<>();
    static
    {
    	//用来模拟数据库
        hashMap.put(1L,new Payment(1L,"28a8c1e3bc2742d8848569891fb42181"));
        hashMap.put(2L,new Payment(2L,"bba8c1e3bc2742d8848569891ac32182"));
        hashMap.put(3L,new Payment(3L,"6ua8c1e3bc2742d8848569891xt92183"));
    }

    @GetMapping(value = "/paymentSQL/{id}")
    public CommonResult<Payment> paymentSQL(@PathVariable("id") Long id)
    {
        Payment payment = hashMap.get(id);
        CommonResult<Payment> result = new CommonResult(200,"from mysql,serverPort:  "+serverPort,payment);
        return result;
    }
}

新建消费者 cloudalibaba-consumer-nacos-order84

pom

	<dependencies>
        <!--SpringCloud openfeign -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>
        <!--SpringCloud ailibaba nacos -->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>
        <!--SpringCloud ailibaba sentinel -->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
        </dependency>
        <!-- 引入自己定义的api通用包,可以使用Payment支付Entity -->
        <dependency>
            <groupId>com.mzr.springcloud</groupId>
            <artifactId>cloud-api-commons</artifactId>
            <version>${project.version}</version>
        </dependency>
        <!-- SpringBoot整合Web组件 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
    </dependencies>

yml

server:
  port: 84

spring:
  application:
    name: nacos-order-consumer
  cloud:
    nacos:
      discovery:
        server-addr: 192.168.254.138:8848
    sentinel:
      transport:
        #配置Sentinel dashboard地址
        dashboard: localhost:8080
        #默认8719端口,假如被占用会自动从8719开始依次+1扫描,直至找到未被占用的端口
        port: 8719

#消费者将要去访问的微服务名称(注册成功进nacos的微服务提供者)
service-url:
  nacos-user-service: http://nacos-payment-provider

主启动类

@EnableDiscoveryClient
@SpringBootApplication
@EnableFeignClients
public class OrderNacosMain84
{
    public static void main(String[] args) {
        SpringApplication.run(OrderNacosMain84.class, args);
    }
}

既然使用ribbon,肯定少不了我们的RestTemplate 配置

@Configuration
public class ApplicationContextConfig
{
    @Bean
    @LoadBalanced
    public RestTemplate getRestTemplate()
    {
        return new RestTemplate();
    }
}

controller

@RestController
@Slf4j
public class CircleBreakerController
{
    public static final String SERVICE_URL = "http://nacos-payment-provider";

    @Resource
    private RestTemplate restTemplate;

    @RequestMapping("/consumer/fallback/{id}")
    @SentinelResource(value = "fallback") //没有配置
    public CommonResult<Payment> fallback(@PathVariable Long id)
    {
        CommonResult<Payment> result = restTemplate.getForObject(SERVICE_URL + "/paymentSQL/"+id,CommonResult.class,id);

		if (id == 4) {
            throw new IllegalArgumentException ("IllegalArgumentException,非法参数异常....");
        }else if (result.getData() == null) {
            throw new NullPointerException ("NullPointerException,该ID没有对应记录,空指针异常");
        }
        return result;
    }
}

启动测试,访问http://localhost:84/consumer/fallback/1发现9004/9003交替出现,负载均衡访问

在这里插入图片描述

服务熔断无配置

看上诉代码得知上述代码中 @SentinelResource(value = “fallback”) 没有任何配置

此时我们访问http://localhost:84/consumer/fallback/4和http://localhost:84/consumer/fallback/5发现返回都是java程序运行异常的error页面,对于用户很不友好

在这里插入图片描述

服务熔断只配置fallback

修改注解配置为@SentinelResource(value = “fallback”,fallback = “handlerFallback”)并添加兜底方法

	@RequestMapping("/consumer/fallback/{id}")
    @SentinelResource(value = "fallback",fallback = "handlerFallback") //fallback只负责业务异常
    public CommonResult<Payment> fallback(@PathVariable Long id)
    {
        CommonResult<Payment> result = restTemplate.getForObject(SERVICE_URL + "/paymentSQL/"+id,CommonResult.class,id);

        if (id == 4) {
            throw new IllegalArgumentException ("IllegalArgumentException,非法参数异常....");
        }else if (result.getData() == null) {
            throw new NullPointerException ("NullPointerException,该ID没有对应记录,空指针异常");
        }

        return result;
    }
    //本例是fallback
    public CommonResult handlerFallback(@PathVariable  Long id,Throwable e) {
        Payment payment = new Payment(id,"null");
        return new CommonResult<>(444,"兜底异常handlerFallback,exception内容  "+e.getMessage(),payment);
    }

重新启动,再次访问http://localhost:84/consumer/fallback/4、和http://localhost:84/consumer/fallback/5

可以发现当前返回已经相对友好些

在这里插入图片描述

服务熔断只配置blockHandler

修改注解配置为@SentinelResource(value = “fallback”,blockHandler = “blockHandler”)并添加sentinel配置

@RequestMapping("/consumer/fallback/{id}")
    @SentinelResource(value = "fallback",blockHandler = "blockHandler") //blockHandler只负责sentinel控制台配置违规
    public CommonResult<Payment> fallback(@PathVariable Long id)
    {
        CommonResult<Payment> result = restTemplate.getForObject(SERVICE_URL + "/paymentSQL/"+id,CommonResult.class,id);

        if (id == 4) {
            throw new IllegalArgumentException ("IllegalArgumentException,非法参数异常....");
        }else if (result.getData() == null) {
            throw new NullPointerException ("NullPointerException,该ID没有对应记录,空指针异常");
        }

        return result;
    }
    //本例是blockHandler
    public CommonResult blockHandler(@PathVariable  Long id,BlockException blockException) {
        Payment payment = new Payment(id,"null");
        return new CommonResult<>(445,"blockHandler-sentinel限流,无此流水: blockException  "+blockException.getMessage(),payment);
    }

在这里插入图片描述

此时访问http://localhost:84/consumer/fallback/4

访问第1次,此时还没有达到我们sentinel降级配置要求,由于程序异常并且没有fallback配置出现不友好的error页面

在这里插入图片描述

快速连续访问多次,此时达到sentinel降级配置触发降级

在这里插入图片描述

服务熔断同时配置fallback及blockHandler

修改注解配置为@SentinelResource(value = “fallback”,fallback = “handlerFallback”,blockHandler = “blockHandler”),并新增sentinel降级配置

	@RequestMapping("/consumer/fallback/{id}")
    @SentinelResource(value = "fallback",fallback = "handlerFallback",blockHandler = "blockHandler")
    public CommonResult<Payment> fallback(@PathVariable Long id)
    {
        CommonResult<Payment> result = restTemplate.getForObject(SERVICE_URL + "/paymentSQL/"+id,CommonResult.class,id);

        if (id == 4) {
            throw new IllegalArgumentException ("IllegalArgumentException,非法参数异常....");
        }else if (result.getData() == null) {
            throw new NullPointerException ("NullPointerException,该ID没有对应记录,空指针异常");
        }

        return result;
    }
    //本例是fallback
    public CommonResult handlerFallback(@PathVariable  Long id,Throwable e) {
        Payment payment = new Payment(id,"null");
        return new CommonResult<>(444,"兜底异常handlerFallback,exception内容  "+e.getMessage(),payment);
    }
    //本例是blockHandler
    public CommonResult blockHandler(@PathVariable  Long id,BlockException blockException) {
        Payment payment = new Payment(id,"null");
        return new CommonResult<>(445,"blockHandler-sentinel限流,无此流水: blockException  "+blockException.getMessage(),payment);
    }

新增sentinel降级配置

在这里插入图片描述

启动测试,访问一次http://localhost:84/consumer/fallback/4,此时并没有达到我们sentinel降级配置要求,但配置了fallback,所以程序异常会调用兜底方法返回错误信息

在这里插入图片描述

快速连续访问2次,此时达到我们sentinel降级配置要求,但同时程序异常会触发调用兜底方法,此时会返回什么呢??很明显返回的是限流信息

在这里插入图片描述

结论:若blockHandler和fallback都进行了配置,则被限流降级而抛出BlockException时只会进入blockHandler处理逻辑。

服务熔断配置exceptionsToIgnore

修改注解配置为@SentinelResource(value = “fallback”,fallback = “handlerFallback”,blockHandler = “blockHandler”,exceptionsToIgnore = {IllegalArgumentException.class})

其他配置与同时配置fallback及blockHandler案例相同

重新启动,访问一次http://localhost:84/consumer/fallback/4

在这里插入图片描述

发现触发程序异常但没有调用我们配置的兜底方法,why???

在这里插入图片描述

Feign环境

修改84微服务,Feign组件一般是在消费侧,使用84消费者调用提供者9003

pom增加openFeign依赖

		<!--SpringCloud openfeign -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>

yml文件激活Sentinel对Feign的支持

# 激活Sentinel对Feign的支持
feign:
  sentinel:
    enabled: true

主启动类添加注解@EnableFeignClients

带@FeignClient注解的业务接口

@FeignClient(value = "nacos-payment-provider",fallback = PaymentFallbackService.class)
public interface PaymentService
{
    @GetMapping(value = "/paymentSQL/{id}")
    public CommonResult<Payment> paymentSQL(@PathVariable("id") Long id);
}

fallback = PaymentFallbackService.class

@Component
public class PaymentFallbackService implements PaymentService
{
    @Override
    public CommonResult<Payment> paymentSQL(Long id)
    {
        return new CommonResult<>(44444,"服务降级返回,---PaymentFallbackService",new Payment(id,"errorSerial"));
    }
}

Controller

@RestController
@Slf4j
public class CircleBreakerController
{
    //==================OpenFeign
    @Resource
    private PaymentService paymentService;

    @GetMapping(value = "/consumer/paymentSQL/{id}")
    public CommonResult<Payment> paymentSQL(@PathVariable("id") Long id)
    {
        return paymentService.paymentSQL(id);
    }
}

启动测试84调用9003,访问 http://localhost:84/consumer/paymentSQL/1,服务当前正常运行

在这里插入图片描述

此时故意关闭9003微服务提供者,看84消费侧是否会自动降级调用兜底方法,还是会被自动耗死

答案是会自动降级调用兜底方法

在这里插入图片描述

注意注意!!
此案例只是演示了Sentinel对openFeign的支持,可以与Hystrix作一下对比看有什么区别,而关于sentinel本身的降级配置在此处并没有演示,大家可以自行配置尝试一下

规则持久化

还记得学习@SentinelResource详解时遇到的一个问题吗

一旦我们应用重启,Sentinel控制台配置的规则消失了? ? ? ?

生产环境需要将配置规则进行持久化

如何持久化

将限流配置规则持久化进Nacos保存,只要刷新8401某个rest地址, sentinel控制台的流控规则就能看到,只要Nacos里面的配置不删除,针对8401上sentinel上的流控规则持续有效

这里使用8401进行演示

RateLimitController新增方法byUrl

@GetMapping("/rateLimit/byUrl")
    @SentinelResource(value = "byUrl")
    public CommonResult byUrl() {
        return new CommonResult(200, "按url限流测试OK", new Payment(2020L, "serial002"));
    }

此时sentinel没有配置,我们正常访问 http://localhost:8401/rateLimit/byUrl

在这里插入图片描述

配置sentinel流控规则

在这里插入图片描述

此时快速连续访问http://localhost:8401/rateLimit/byUrl,会发现此时流控规则已经生效

在这里插入图片描述

此时我们将微服务重新启动,发现sentinel控制台配置信息已经没有了

如何持久化呢??

修改8401微服务

pom添加依赖

<!--SpringCloud ailibaba sentinel-datasource-nacos 后续做持久化用到-->
        <dependency>
            <groupId>com.alibaba.csp</groupId>
            <artifactId>sentinel-datasource-nacos</artifactId>
        </dependency>

yml添加配置信息

server:
  port: 8401

spring:
  application:
    name: cloudalibaba-sentinel-service
  cloud:
    nacos:
      discovery:
        #Nacos服务注册中心地址,使用集群地址(可以看我上一章关于nacos笔记)
        server-addr: 192.168.254.138:8848
    sentinel:
      transport:
        dashboard: localhost:8080 #配置Sentinel dashboard地址
        #默认8719端口,假如被占用会自动从8719开始依次+1扫描,直至找到未被占用的端口
        port: 8719
      datasource:
        ds1:
          nacos:
            server-addr: 192.168.254.138:8848
            dataId: cloudalibaba-sentinel-service
            groupId: DEFAULT_GROUP
            data-type: json
            rule-type: flow

添加Nacos业务规则配置

在这里插入图片描述

json串的含义
resource:资源名称
limitApp:来源应用
grade:阈值类型,0表示线程数,1表示QPS
count:单机阈值
strategy:流控模式,0表示直接,1表示关联,2表示链路
controlBehavior:流控效果,0表示快速失败,1表示Warm Up,2表示排队等待
clusterMode:是否集群

启动8401后刷新sentinel发现业务规则有了(没有就先访问http://localhost:8401/rateLimit/byUrl即可)

在这里插入图片描述

快速多次访问测试接口,发现流控规则已经生效

在这里插入图片描述

停止8401再看sentinel,发现sentinel控制台流控规则消失了

在这里插入图片描述

重新启动8401先访问http://localhost:8401/rateLimit/byUrl 再刷新sentinel页面

在这里插入图片描述

重新配置出现了,持久化验证通过~

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

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

相关文章

JVM中虚拟机栈和本地方法栈等

jvm Java虚拟机栈本地方法栈 Java虚拟机栈 Java虚拟机栈&#xff08;VM Stack&#xff09; ​ 虚拟机栈是线程执行Java程序时&#xff0c;处理Java方法中内容的内存区域。虚拟机栈也是线程私有的区域&#xff0c;每个Java方法被调用的时候&#xff0c;都会在虚拟机栈中创建出…

实战-docker方式给自己网站部署prometheus监控ecs资源使用情况-2024.1.7(测试成功)

title: 实战-docker方式给自己网站部署prometheus监控ecs资源使用情况-2024.1.7(测试成功) date: 2024-1-7 categories: linux tags: promtheues summary: prometheusgrafana 更新于&#xff1a;2024年1月7日 实战-docker方式给自己网站部署prometheus监控ecs资源使用情况-2024…

二叉树与堆的深度解析:数据结构中的关键概念及应用

. 个人主页&#xff1a;晓风飞 专栏&#xff1a;数据结构|Linux|C语言 路漫漫其修远兮&#xff0c;吾将上下而求索 文章目录 前言树概念注意&#xff1a; 树的基本概念及术语基本概念及术语以家谱为例 树的表示孩子兄弟表示法简介优势应用示例 树在实际中的运用文件系统的目录树…

【Maven笔记3】Maven基础入门案例

本篇通过一个最基础的入门案例&#xff0c;熟悉一下maven最基础的使用方法。 编写POM maven项目的核心是pom.xml文件&#xff0c;pom定义了项目的基本信息&#xff0c;用于描述项目如何构建&#xff0c;声明项目依赖等等。 这里我们新建一个maven-demo-hello项目&#xff0c;…

VS Code结合Live Server插件快速搭建小游戏并发布至公网可随时远程访问

文章目录 前言1. 编写MENJA小游戏2. 安装cpolar内网穿透3. 配置MENJA小游戏公网访问地址4. 实现公网访问MENJA小游戏5. 固定MENJA小游戏公网地址 前言 本篇教程&#xff0c;我们将通过VS Code实现远程开发MENJA小游戏&#xff0c;并通过cpolar内网穿透发布到公网&#xff0c;分…

黑莓系统的安全性如何?

黑莓系统的安全性非常高&#xff01; 在过去很长一段时间里&#xff0c;都被认为是手机市场上最安全的操作系统。这主要得益于黑莓在安全性方面的重视和投入。 &#xff08;在世界上最安全的 6 款手机排名中&#xff0c;iPhone未能入围&#xff09; 世界上最安全的 6 款手机&…

CNN——ResNet

深度残差网络&#xff08;Deep residual network, ResNet&#xff09;的提出是CNN图像史上的一件里程碑事件&#xff0c;并且让深度学习真正可以继续做下去&#xff0c;斩获2016 CVPR Best Paper。此外ResNet的作者都是中国人&#xff0c;一作何恺明。ResNet被提出以后很多的网…

Mac 16g约等于Windows多少g?

Mac 16g 内存等于 Windows 320g 内存 何为“黄金内存”&#xff1f; Mac 的内存是用黄金做的&#xff0c;而 Windows 的内存是用铁做的。 黄金的密度是 19.32 g/cm&#xff0c;而铁的密度是 7.874 g/cm。 因此&#xff0c;16g 的黄金体积是 0.082 cm&#xff0c;而 16g 的铁…

交换机_05VLAN

一、VLAN技术的引入 VLAN&#xff08;Virtual Lan&#xff09;主要应用在交换机上 一台交换机默认情况下连接一个广播域&#xff0c;因为默认情况下所有的接口都是属于同一个vlan的&#xff0c;默认vlan1&#xff0c;所以是在同一个广播域中。 结合交换机工作原理&#xff0…

Python 利用PYQT5设计基于RSA算法盲签名的匿名化电子支付系统设计与实现

基于RSA算法的盲签名算法 David Chaum 于1982年提出盲签名的概念&#xff0c;并利用RSA算法设计了第一个盲签名方案. 该方案的安全性基于大整数分解问题 盲签名的步骤 1.密钥生成 签名者执行以下步骤生成密钥对: ①签名者选择两个大素数p,q&#xff0c; 计算npq&#xff0…

OpenCV-18图像的翻转和旋转

一、图像的翻转 使用API---cv.flip&#xff08;src, flipCode&#xff09; flipCode 0表示上下翻转 flipCode > 0表示左右翻转 flipCode < 0上下 左右翻转 或者使用np的翻转src[: : -1,: : -1]实现上下翻转。 示例代码如下&#xff1a; import cv2 import numpy…

GPT(Generative Pre-Training)论文解读及源码实现(二)

本篇为gpt2的pytorch实现&#xff0c;参考 nanoGPT nanoGPT如何使用见后面第5节 1 数据准备及预处理 data/shakespeare/prepare.py 文件源码分析 1.1 数据划分 下载数据后90%作为训练集&#xff0c;10%作为验证集 with open(input_file_path, r) as f:data f.read() n …

yolo 分割label格式标注信息图片显示可视化查看

参考: https://github.com/ultralytics/ultralytics/issues/3137 https://blog.csdn.net/weixin_42357472/article/details/135218349?spm=1001.2014.3001.5501 需要把坐标信息在图片上显示 代码 1)只画出了坐标边缘 import cv2 import numpy as np from random impor…

html 原生网页使用ElementPlus 日期控件el-date-picker换成中文

项目&#xff1a; 原生的html,加jQuery使用不习惯&#xff0c;新html页面导入vue3,element plus做界面&#xff0c;现在需要把日历上英文切成中文。 最终效果&#xff1a; 导入能让element plus日历变成中文脚本&#xff1a; elementplus, vue3对应的js都可以通过创建一个vu…

idea 以文本形式输出 SpringBoot项目 目录结构

第1步&#xff1a;AltF12 打开 Terminal 终端 第2步&#xff1a;cd 到 项目路径下 第3步&#xff1a;使用 tree 命令 结果 D:. ├─.mvn │ └─wrapper ├─applog │ └─logs ├─src │ ├─main │ │ ├─java │ │ │ └─com │ │ │ └─zhangziwa …

【软件测试】学习笔记-如何做好单元测试

什么是单元测试&#xff1f; 在正式开始今天的话题之前&#xff0c;我先给你分享一个工厂生产电视机的例子。 工厂首先会将各种电子元器件按照图纸组装在一起构成各个功能电路板&#xff0c;比如供电板、音视频解码板、射频接收板等&#xff0c;然后再将这些电路板组装起来构…

【计算机网络】网络编程套接字socket--UDP/TCP简单服务器实现/TCP协议通信流程

文章目录 一、预备知识1.IP和端口号2.TCP协议和UDP协议3.网络字节序 二、socket编程接口1.socket 常见API2.sockaddr结构 三、UDP服务器相关重要接口介绍sendtorecvfrompopen 1.udpServer.hpp2.udpServer.cc3.udpClient.hpp4.udpClient.cc5.onlineUser.hpp 四、TCP服务器socket…

高性能、可扩展、分布式对象存储系统MinIO的介绍、部署步骤以及代码示例

详细介绍 MinIO 是一款流行的开源对象存储系统&#xff0c;设计上兼容 Amazon S3 API&#xff0c;主要用于私有云和边缘计算场景。它提供了高性能、高可用性以及易于管理的对象存储服务。以下是 MinIO 的详细介绍及优缺点&#xff1a; 架构与特性&#xff1a; 开源与跨平台&am…

HTML---JavaScript操作DOM对象

文章目录 前言一、pandas是什么&#xff1f;二、使用步骤 1.引入库2.读入数据总结 本章目标 了解DOM的分类和节点间的关系熟练使用JavaScript操作DOM节点 访问DOM节点 能够熟练的进行节点的创建、添加、删除、替换等 能够熟练的设置元素的样式 能够灵活运用JavaScript获取元素…

SpringBoot学习(八)-SpringBoot + Dubbo + zookeeper

分布式DubboZookeeper 1、分布式理论 1&#xff09;什么是分布式系统&#xff1f; 在《分布式系统原理与范型》一书中有如下定义&#xff1a;“分布式系统是若干独立计算机的集合&#xff0c;这些计算机对于用户来说就像单个相关系统”&#xff1b; 分布式系统是由一组通过…