SpringCloud微服务(十一)——Sentinel服务熔断限流

news2024/11/17 5:36:19

SpringCloud Alibaba Sentinel服务熔断与限流

简介

github:[https://github.com/alibaba/Sentinel/wiki/%E4%B8%BB%E9%A1%B5](https://github.com/alibaba/Sentinel/wiki/如何使用)

官网:https://spring-cloud-alibaba-group.github.io/github-pages/greenwich/spring-cloud-alibaba.html#_spring_cloud_alibaba_sentinel

一句话解释,跟Hystrix一样的理念

随着微服务的流行,服务和服务之间的稳定性变得越来越重要。Sentinel 是面向分布式服务架构的轻量级流量控制组件,主要以流量为切入点,从限流、流量整形、熔断降级、系统负载保护等多个维度来帮助您保障微服务的稳定性。

在这里插入图片描述

流量控制

流量控制在网络传输中是一个常用的概念,它用于调整网络包的发送数据。然而,从系统稳定性角度考虑,在处理请求的速度上,也有非常多的讲究。任意时间到来的请求往往是随机不可控的,而系统的处理能力是有限的。我们需要根据系统的处理能力对流量进行控制。Sentinel 作为一个调配器,可以根据需要把随机的请求调整成合适的形状。

熔断降级

除了流量控制以外,及时对调用链路中的不稳定因素进行熔断也是 Sentinel 的使命之一。由于调用关系的复杂性,如果调用链路中的某个资源出现了不稳定,可能会导致请求发生堆积,进而导致级联错误。

系统负载保护

Sentinel 同时提供系统维度的自适应保护能力。防止雪崩,是系统防护中重要的一环。当系统负载较高的时候,如果还持续让请求进入,可能会导致系统崩溃,无法响应。在集群环境下,网络负载均衡会把本应这台机器承载的流量转发到其它的机器上去。如果这个时候其它的机器也处在一个边缘状态的时候,这个增加的流量就会导致这台机器也崩溃,最后导致整个集群不可用。

针对这个情况,Sentinel 提供了对应的保护机制,让系统的入口流量和系统的负载达到一个平衡,保证系统在能力范围之内处理最多的请求。

安装

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

下载的是sentinel-dashboard-1.7.1.jar

#默认8080端口,8080不要被占用
#直接运行即可,需要jdk环境
java -jar sentinel-dashboard-1.7.1.jar

#linux系统
nohup java -jar sentinel-dashboard-1.7.1.jar &
ctrl+c
cat nohup.out
#访问8080端口
#账号sentinel,密码sentinel

在这里插入图片描述

演示工程

依赖配置

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

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

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

yml配置

server:
  port: 8401

spring:
  application:
    name: cloudalibaba-sentinel-service
  cloud:
    nacos:
      discovery:
        # Nacos服务注册中心地址
        server-addr: localhost:8848
    sentinel:
      transport:
        # sentinel dashboard 地址
        dashboard: 192.168.169.130:8080
        # 默认为8719,如果被占用会自动+1,直到找到为止
        # 簇点链路,可以把某些微服务归类到某个链路,统一处理
        port: 8719
        
      # 流控规则持久化到nacos
      datasource:
        dsl:
          nacos:
            server-addr: localhost:8848
            data-id: ${spring.application.name}
            group-id: DEFAULT_GROUP
            data-type: json
            rule-type: flow
            
management:
  endpoints:
    web:
      exposure:
        include: "*"

启动类使用的是nacos注册中心@EnableDiscoveryClient

启动,访问Rest请求,可以在sentinel管理页面看到确实有监控cloudalibaba-sentinel-service微服务:

如果不访问该微服务的rest请求或者长时间没有访问该微服务,sentinel管理页面会去掉对该微服务接口的监控。要是页面找不到就访问下资源接口。

在这里插入图片描述

流控规则

流量控制

就是限流,限制资源接口的访问数

官网:https://github.com/alibaba/Sentinel/wiki/%E6%B5%81%E9%87%8F%E6%8E%A7%E5%88%B6

  • 资源名:唯一名称,默认请求路径,就是restcontroller上的路径
  • 针对来源:Sentinel可以针对调用者进行限流,填写微服务名,默认default(不区分来源)
  • 阈值类型/单机阈值
    • QPS(每秒钟的请求数量):当调用该api的QPS达到阈值的时候,进行限流
    • 线程数:当调用该api的线程数达到阈值的时候,进行限流
    • 2者区别就是QPS会让请求全部过来访问,线程数最多只能过来对应线程数数量的请求数,来多了也没用
  • 是否集群:不需要集群
  • 流控模式:
    • 直接:api达到限流条件,直接限流
    • 关联:当关联的资源达到阈值时,就限流自己
    • 链路:只记录指定链路上的流量(指定资源从入口资源进来的流量,如果达到阈值,就进行限流)(api级别的针对来源)
  • 流控效果:
    • 快速失败:直接失败,抛异常
    • Warm Up:根据codeFactor(冷加载因子,默认3)的值,从阈值/codeFactor,经过预热时长,才达到设置的QPS阈值
    • 排队等待:匀速排队,让请求以匀速的速度通过,阈值类型必须设置为QPS,否则无效

流控模式

    @GetMapping("/testA")
    public String testA(){
        try {
            //0.8秒
            TimeUnit.MILLISECONDS.sleep(800);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return "testA-----";
    }

    @GetMapping("/testB")
    public String testB(){
        log.info(Thread.currentThread().getName() + "...testB ");
        return "testB   -----";
    }
直接(默认)

系统默认直接快速失败,超过阈值直接限流

阈值类型:QPS或线程数

如下设置路径为/testA的流控:

单机阈值是每秒1个访问/每秒一个线程数

在这里插入图片描述

快速点击访问http://localhost:8401/testA

手速超过每秒1个访问/每秒一个线程数,就会限制访问。

在这里插入图片描述

关联

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

当与A关联的资源B达到阈值后,就限流自己

举例子:支付接口遭到大量访问,且很多未处理,那么我们应该限制下订单的接口访问,不然支付接口访问会堆积更多,支付接口会挂。

当关联资源/testB的QPS阈值超过1时,就限流/testA的Rest访问地址,当关联资源到达阈值后限制配置好的的资源名。

在这里插入图片描述

postman模拟并发密集访问testB,jmeter也行。

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

这样就可以大量访问/testB,在网页访问/testA,将无法访问,被限流

在这里插入图片描述

链路

多个请求调用了同一个微服务

簇点链路端口默认为8719,如果被占用会自动+1,直到找到为止

簇点链路,可以把某些微服务归类到某个链路端口,统一处理

NodeSelectorSlot 中记录了资源之间的调用链路,这些资源通过调用关系,相互之间构成一棵调用树。这棵树的根节点是一个名字为 machine-root 的虚拟节点,调用链的入口都是这个虚节点的子节点。

一棵典型的调用树如下图所示:

     	          machine-root
                    /       \
                   /         \
             Entrance1     Entrance2
                /             \
               /               \
      DefaultNode(nodeA)   DefaultNode(nodeA)

上图中来自入口 Entrance1Entrance2 的请求都调用到了资源 NodeA,Sentinel 允许只根据某个入口的统计信息对资源限流。比如我们可以设置 FlowRule.strategyRuleConstant.CHAIN,同时设置 FlowRule.ref_identityEntrance1 来表示只有从入口 Entrance1 的调用才会记录到 NodeA 的限流统计当中,而不关心经 Entrance2 到来的调用。

调用链的入口(上下文)是通过 API 方法 ContextUtil.enter(contextName) 定义的,其中 contextName 即对应调用链路入口名称。

流控效果

直接(默认)

快速失败(默认的流控处理)

直接失败,抛出异常

Blocked by Sentinel(flow limiting)页面

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

预热

公式:阈值除以coldFactor(默认值为3),经过预热时长后才会达到阈值

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

限流冷启动:https://github.com/alibaba/Sentinel/wiki/

在这里插入图片描述

系统初始化的阈值为10/3约等于3,即阈值刚开始为3;然后过了5秒后阈值才慢慢升高恢复到10。

多次点击http://localhost:8401/testB

一开始点太快会限流Blocked by Sentinel(flow limiting),5秒后10阈值,每秒10个内都可以访问。

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

排队等待

匀速排队,阈值必须设置为QPS

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

让请求均匀的速度通过,阈值必须设置为QPS,否则无效

如下,/testA每秒1次请求,超过的话排队等待,等待的超时时间为20000毫秒。

在这里插入图片描述

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

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

postman模拟并发密集访问testA

controller打印日志

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

每秒一个:

在这里插入图片描述

降级规则

服务降级,服务超时或异常给出友好提示或兜底方案,实际开发需要自定义返回,这里先测试。

官网:https://github.com/alibaba/Sentinel/wiki/熔断降级

Sentinel熔断降级会在调用链路中某个资源出现不稳定状态时(例如调用超时或异常比例升高),对这个资源的调用进行限制,让请求快速失败,避免影响到其它的资源而导致级联错误。

当资源被降级后,在接下来的降级时间窗口之内,对该资源的调用都自动熔断(默认抛出DegradeException)

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

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

    平均响应时间:超出阈值在时间窗口内通过的请求>=5,有两个条件同时满足后触发降级

    窗口期过后关闭断路器

    RT最大4900(更大的需要通过-Dcsp.sentinel.statistic.max.rt=XXXX才能生效)

  • 异常比例(秒级)

    QPS>=5且异常比例(秒级统计)超过阈值时,触发降级;时间窗口结束后,关闭降级

  • 异常数(分钟级)

    异常数(分钟统计)超过阈值时,触发降级;时间窗口结束后,关闭降级

降级策略实战

RT

平均响应时间 (DEGRADE_GRADE_RT):当 1s 内持续进入 N 个请求,对应时刻的平均响应时间(秒级)均超过阈值(count,以 ms 为单位),那么在接下的时间窗口(DegradeRule 中的 timeWindow,以 s 为单位)之内,对这个方法的调用都会自动地熔断(抛出 DegradeException)。注意 Sentinel 默认统计的 RT 上限是 4900 ms,超出此阈值的都会算作 4900 ms,若需要变更此上限可以通过启动配置项 -Dcsp.sentinel.statistic.max.rt=xxx 来配置。

在这里插入图片描述

对controller睡眠1秒测试TimeUnit.SECONDS.sleep(1);,对资源选择降级设置:(条件必须每秒请求超过5个,且超过阈值)

在这里插入图片描述

使用jmeter压力测试

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

执行,一秒10个请求>5,请求均超过阈值200ms,未来的时间窗口被降级,jmeter一直执行,一直降级,关闭jmeter,即可恢复。

异常比例

异常比例 (DEGRADE_GRADE_EXCEPTION_RATIO):当资源的每秒请求量 >= N(可配置),并且每秒异常总数占通过量的比值超过阈值(DegradeRule 中的 count)之后,资源进入降级状态,即在接下的时间窗口(DegradeRule 中的 timeWindow,以 s 为单位)之内,对这个方法的调用都会自动地返回。异常比率的阈值范围是 [0.0, 1.0],代表 0% - 100%。

在这里插入图片描述

在对应controller加上异常int age = 10 /0 ;测试,访问,如下:

在这里插入图片描述

条件和设置:

在这里插入图片描述

jmeter高并发测试,再次网页访问,不是上次的报错页面了,而是降级:

在这里插入图片描述

异常数

异常数是按分钟统计的

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

在这里插入图片描述

在对应controller加上异常int age = 10 /0 ;测试,访问,如下:

在这里插入图片描述

接着设置异常数降级规则:

在这里插入图片描述

然后网页访问请求,前5次都是报错的页面,第6次后都是降级页面,降级页面会持续70秒,70秒过后恢复。

在这里插入图片描述

热点规则

也是限流措施,热点key,对某个访问量高的参数值进行限流。

官网:https://github.com/alibaba/Sentinel/wiki/热点参数限流

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

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

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

在这里插入图片描述

Sentinel 利用 LRU 策略统计最近最常访问的热点参数,结合令牌桶算法来进行参数级别的流控。热点参数限流支持集群模式。

兜底方案:

分为系统默认和客户自定义,两种

之前的case,限流出问题后,都是sentinel系统默认的提示:Blocked by Sentinel(flow limiting)

我们也可以自定义,类似hystrix的fallback降级兜底方法,结论是从@HystrixCommand—>>>@SentinelResource

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

默认是出错就抛异常BlockException处理。

普通设置

测试代码@RestController:

@SentinelResource(value = "testHotKey", blockHandler = "dealTestHotKey")

设置热点key的异常方法,就是热点key被触发限流了就跳到dealTestHotKey方法。通过BlockException异常来处理。

    @GetMapping("/testHotKey")
    @SentinelResource(value = "testHotKey", blockHandler = "dealTestHotKey") //value自定义,唯一就行,尽量保持跟路径一致
    public String testHotKey(@RequestParam(value = "p1", required = false) String p1,
                             @RequestParam(value = "p2", required = false) String p2){
        //int age = 10 /0;
        return "testHotKey -----";
    }

    //BlockException blockException必须加,兜底方法
    public String dealTestHotKey(String p1, String p2, BlockException blockException){
        return "dealTestHotKey---------";
    }

sentinel上设置热点key:

testHotKey是注解上的名字,保持一致。

在这里插入图片描述

索引0开始,对应资源接口上的参数,也就是p1。

只要参数中没有p1带参,无论怎么访问都是没问题的。

在这里插入图片描述

如果带参p1,只要符合,不超过阈值也可以正常响应,如果1s内QPS访问次数超过阈值1时,则报错。当然这是自定义的报错页面,因为我们加了blockHandler = "dealTestHotKey"方法

在这里插入图片描述

去掉blockHandler = "dealTestHotKey"方法,则是如下页面

在这里插入图片描述

不是Blocked by Sentinel(flow limiting),热点key只处理Sentinel页面上的规则问题,如果代码加入int age = 10/0;等模拟异常代码,错误信息会直接在页面显示,不处理其他异常的降级,只处理这个BlockException异常,所以热点key必须加上兜底方法。

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

int age = 10/0;这个是java运行报出的运行时异常RunTimeException.@SentinelResource不管

@SentinelResource主管配置出错,运行出错自己走异常。

参数额外项

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

我们期望p1参数当它是某个特殊值时,它的限流和平时不一样

比如:特例:假如当p1的值等于5时,它的阈值可以达到200

如下设置:

参数类型对应rest接口的p1参数类型

在这里插入图片描述

当p1=5时,连续快速测试访问都没触发降级,当p1不等于5的时候,阈值变为平常的1。当p1等于5的时候,阈值变为200。

在这里插入图片描述

系统规则

说白了就是这个系统规则会对所有rest接口生效。全局

官网:https://github.com/alibaba/Sentinel/wiki/系统自适应限流

Sentinel 系统自适应限流从整体维度对应用入口流量进行控制,结合应用的 Load、CPU 使用率、总体平均 RT、入口 QPS 和并发线程数等几个维度的监控指标,通过自适应的流控策略,让系统的入口流量和系统的负载达到一个平衡,让系统尽可能跑在最大吞吐量的同时保证系统整体的稳定性。

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

在这里插入图片描述

比如选择入口QPS的阈值为1,则所有的请求都是每秒只能请求一次,不然就降级。

@SentinelResource

按资源名称+后续处理

测试自带的blockHandler异常方法

@RestController
public class RateLimitController {

    @GetMapping("/byResource")
    @SentinelResource(value = "byResource", blockHandler = "handleException")//名字可自定义,不唯一就行
    public CommonResult byResource(){
        return new CommonResult(200, "按资源名称限流测试OK", new Payment(2020L, IdUtil.simpleUUID()));//hutool的工具包,生成UUID不带-
    }
    
    public CommonResult handleException(BlockException blockException){
        // 打印哪个异常方法在限流处理
        return new CommonResult<>(444, blockException.getClass().getCanonicalName()+"\t服务不可用" );
    }
}

用@SentinelResource上的名字byResource设置流控规则,才会调到自定义方法,如果超过sentinel控制台设置的阈值,跳到自定义的方法:

在这里插入图片描述

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

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

通过访问的URL限流,会返回Sentinel自带默认的限流处理信息

通过路径url设置流控规则,超过阈值:

在这里插入图片描述

上面兜底方案面临的问题

  • 系统默认的,没有体现我们自己的业务要求
  • 依照现有条件,我们自定义的处理方法又和业务代码耦合在一块,不直观
  • 每个业务方法都添加一个兜底的,那代码膨胀
  • 全局统一的处理方法没有体现

客户自定义限流处理逻辑

实际开发常用

这是争对sentinel控制规则违规的兜底,程序异常不行

自定义限流处理类CustomerBlockHandler,参数必须得加上BlockException

package com.wzq.springcloud.myhandler;

import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.wzq.springcloud.entities.CommonResult;

/**
 * 自定义全局异常方法,方法可被不同的接口使用
 * @author wzq
 * @version 1.0
 * @create 2020/03/06
 */
public class CustomerBlockHandler {

    public static CommonResult handlerException(BlockException exception) {
        return new CommonResult(444, "客户自定义,global handlerException---1");
    }

    public static CommonResult handlerException2(BlockException exception) {
        return new CommonResult(444, "客户自定义,global handlerException---2");
    }
}

contoller调用

    //CustomerBlockHandler

    @GetMapping("/rateLimit/customerBlockHandler")
    @SentinelResource(value = "customerBlockHandler",
            blockHandlerClass = CustomerBlockHandler.class, blockHandler = "handlerException2")
    public CommonResult customerBlockHandler(){
        return new CommonResult(200, "客户自定义 限流测试OK", new Payment(2020L, IdUtil.simpleUUID()));
    }

blockHandlerClass = CustomerBlockHandler.class, blockHandler = "handlerException2")指定自定义限流类中的某个方法来处理。方法可多次被不用controller调用,解耦。

超过流控阈值:

在这里插入图片描述

注意点

注意:注解方式埋点不支持 private 方法。

@SentinelResource 用于定义资源,并提供可选的异常处理和 fallback 配置项。 @SentinelResource 注解包含以下属性:

  • value:资源名称,必需项(不能为空)

  • entryType:entry 类型,可选项(默认为 EntryType.OUT

  • blockHandler / blockHandlerClass: blockHandler 对应处理 BlockException 的函数名称,可选项。blockHandler 函数访问范围需要是 public,返回类型需要与原方法相匹配,参数类型需要和原方法相匹配并且最后加一个额外的参数,类型为 BlockException。blockHandler 函数默认需要和原方法在同一个类中。若希望使用其他类的函数,则可以指定 blockHandlerClass 为对应的类的 Class 对象,注意对应的函数必需为 static 函数,否则无法解析。

  • fallback

    :fallback 函数名称,可选项,用于在抛出异常的时候提供 fallback 处理逻辑。fallback 函数可以针对所有类型的异常(除了exceptionsToIgnore

    里面排除掉的异常类型)进行处理。fallback 函数签名和位置要求:

    • 返回值类型必须与原函数返回值类型一致;
    • 方法参数列表需要和原函数一致,或者可以额外多一个 Throwable 类型的参数用于接收对应的异常。
    • fallback 函数默认需要和原方法在同一个类中。若希望使用其他类的函数,则可以指定 fallbackClass 为对应的类的 Class 对象,注意对应的函数必需为 static 函数,否则无法解析。
  • defaultFallback

    (since 1.6.0):默认的 fallback 函数名称,可选项,通常用于通用的 fallback 逻辑(即可以用于很多服务或方法)。默认 fallback 函数可以针对所有类型的异常(除了exceptionsToIgnore里面排除掉的异常类型)进行处理。若同时配置了 fallback 和 defaultFallback,则只有 fallback 会生效。defaultFallback 函数签名要求:

    • 返回值类型必须与原函数返回值类型一致;
    • 方法参数列表需要为空,或者可以额外多一个 Throwable 类型的参数用于接收对应的异常。
    • defaultFallback 函数默认需要和原方法在同一个类中。若希望使用其他类的函数,则可以指定 fallbackClass 为对应的类的 Class 对象,注意对应的函数必需为 static 函数,否则无法解析。
  • exceptionsToIgnore(since 1.6.0):用于指定哪些异常被排除掉,不会计入异常统计中,也不会进入 fallback 逻辑中,而是会原样抛出。

注:1.6.0 之前的版本 fallback 函数只针对降级异常(DegradeException)进行处理,不能针对业务异常进行处理

特别地,若 blockHandler 和 fallback 都进行了配置,则被限流降级而抛出 BlockException 时只会进入 blockHandler 处理逻辑。若未配置 blockHandlerfallbackdefaultFallback,则被限流降级时会将 BlockException 直接抛出(若方法本身未定义 throws BlockException 则会被 JVM 包装一层 UndeclaredThrowableException)。

Sentinel主要有三个核心Api:SphU定义资源、Tracer定义统计、ContextUtil定义了上下文

服务熔断

提供者9003/9004,设置2个一样的微服务9003/9004

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

server:
  port: 9003/9004

spring:
  application:
    name: nacos-payment-provider
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848

management:
  endpoints:
    web:
      exposure:
        include: "*"

启动类:@EnableDiscoveryClient

controller接口:

@RestController
public class PaymentController {

    @Value("${server.port}")
    private String serverPort;

    public static Map<Long , Payment> hashMap = new HashMap<>();
    
    static {
        hashMap.put(1L, new Payment(1L, IdUtil.simpleUUID()));
        hashMap.put(2L, new Payment(2L, IdUtil.simpleUUID()));
        hashMap.put(3L, new Payment(3L, IdUtil.simpleUUID()));
    }

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

消费者84

        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>
        <!--     sentinel-datasource-nacos 后续持久化用   -->
        <dependency>
            <groupId>com.alibaba.csp</groupId>
            <artifactId>sentinel-datasource-nacos</artifactId>
        </dependency>
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
        </dependency>

server:
  port: 84
spring:
  application:
    name: nacos-order-consumer
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848
    sentinel:
      transport:
        dashboard: 192.168.169.130:8080
        port: 8719

service-url:
  nacos-user-service: http://nacos-payment-provider

启动类:@EnableDiscoveryClient

Ribbon系列

负载均衡

消费者84添加rest负载均衡:

@Configuration
public class ApplicationContextConfig {

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

通过服务名调用方法后实现轮询。

无配置

controller调用提供者接口:

@RestController
@Slf4j
public class CircleBreakerController {
    
    private 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("id") Long id){
        CommonResult<Payment> commonResult = restTemplate.getForObject(SERVICE_URL + "/paymentSQL/" + id, CommonResult.class);
        if(id == 4){
            throw new IllegalArgumentException("IllegalArgumentException,非法参数异常");
        }else if(commonResult.getData() == null){
            throw new NullPointerException("NullPointerException,该ID没有记录,空指针异常");
        }
        return commonResult;
    }

@SentinelResource(value = "fallback") //没有配置

降级都没配置,默认访问,采用ribbon轮询负载均衡:

在这里插入图片描述

id = 4,测试程序异常,error页面:

在这里插入图片描述

id = 5,测试程序异常,error页面:

在这里插入图片描述

只配置程序异常fallback处理
    @RequestMapping("/consumer/fallback/{id}")
    @SentinelResource(value = "fallback",fallback = "handlerFallback") //配置了fallback的,fallback只负责业务异常
    public CommonResult<Payment> fallback(@PathVariable("id") Long id){
        CommonResult<Payment> commonResult = restTemplate.getForObject(SERVICE_URL + "/paymentSQL/" + id, CommonResult.class);
        if(id == 4){
            throw new IllegalArgumentException("IllegalArgumentException,非法参数异常");
        }else if(commonResult.getData() == null){
            throw new NullPointerException("NullPointerException,该ID没有记录,空指针异常");
        }
        return commonResult;
    }

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

@SentinelResource(value = "fallback",fallback = "handlerFallback") //配置了fallback的,fallback只负责业务异常

fallback只处理程序异常的兜底,出错则跳到handlerFallback方法,测试程序异常,不再是error页面:

在这里插入图片描述

只配置sentinel控制台违规异常blockHandler处理
    @RequestMapping("/consumer/fallback/{id}") 
    @SentinelResource(value = "fallback",blockHandler = "blockHandler") 配置了blockHandler,只负责sentinel控制台配置违规 
    public CommonResult<Payment> fallback(@PathVariable("id") Long id){
        CommonResult<Payment> commonResult = restTemplate.getForObject(SERVICE_URL + "/paymentSQL/" + id, CommonResult.class);
        if(id == 4){
            throw new IllegalArgumentException("IllegalArgumentException,非法参数异常");
        }else if(commonResult.getData() == null){
            throw new NullPointerException("NullPointerException,该ID没有记录,空指针异常");
        }
        return commonResult;
    }

    public CommonResult blockHandler(Long id, BlockException exception){
        Payment payment = new Payment(id, null);
        return new CommonResult<>(445, "blockHandler-sentinel 限流,无此流水号:blockException" + exception.getMessage(), payment);
    }

@SentinelResource(value = "fallback",blockHandler = "blockHandler") 配置了blockHandler,只负责sentinel控制台配置违规

blockHandler只处理sentinel控制台中配置的限流规则违规异常,先配置测试限流,名字是fallback那个:

在这里插入图片描述

前2次访问还是error页面,之后访问就触发了blockHandler兜底方法:

在这里插入图片描述

fallback和blockHandler都配置
    @RequestMapping("/consumer/fallback/{id}")
    @SentinelResource(value = "fallback",fallback = "handlerFallback", blockHandler = "blockHandler")// 配置了blockHandler和fallback
    public CommonResult<Payment> fallback(@PathVariable("id") Long id){
        CommonResult<Payment> commonResult = restTemplate.getForObject(SERVICE_URL + "/paymentSQL/" + id, CommonResult.class);
        if(id == 4){
            throw new IllegalArgumentException("IllegalArgumentException,非法参数异常");
        }else if(commonResult.getData() == null){
            throw new NullPointerException("NullPointerException,该ID没有记录,空指针异常");
        }
        return commonResult;
    }

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

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

@SentinelResource(value = "fallback",fallback = "handlerFallback", blockHandler = "blockHandler")// 配置了blockHandler和fallback

配置了blockHandler和fallback

设置流控规则:

在这里插入图片描述

1秒一个正常访问,但是超过阈值,调用blockHandler方法:

在这里插入图片描述

程序异常测试,调用fallback方法:

在这里插入图片描述

但是访问次数超过阈值,一样还会报blockHandler方法:

在这里插入图片描述

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

忽略属性
@SentinelResource(value = "fallback",fallback = "handlerFallback", blockHandler = "blockHandler",exceptionsToIgnore = {IllegalArgumentException.class}) // 配置了blockHandler和fallback

exceptionsToIgnore={异常1,异常2,…}

这样可以忽略掉某些程序异常,就是这些忽略的异常报错了,不会跳到fallback兜底方法,正常error页面。

Feign系列

新建提供者的feign模块,只需一个模块

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

直接写接口,无需配置和启动类:

接口跟服务提供者的controller一样

/**
 * @author wzq
 * @version 1.0
 * @date 2020/03/07
 */
@FeignClient(value = "nacos-payment-provider", fallback = PaymentFallback.class)
public interface PaymentFeign {

    @GetMapping("/paymentSQL/{id}")
    CommonResult<Payment> paymentSQL(@PathVariable("id") Long id);
}

Fallback熔断实现,对应该接口的方法:

/**
 * @author wzq
 * @version 1.0
 * @date 2020/03/07
 */
@Component
public class PaymentFallback implements PaymentFeign {
    
    // 熔断
    @Override
    public CommonResult<Payment> paymentSQL(Long id) {
        return new CommonResult<>(444, "fallback");
    }
    
}

服务消费者:

引入需要feign接口的模块依赖

启动类加@EnableFeignClients

yml配置加:

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

contoller调用:

    @Resource
    private PaymentFeign paymentFeign;

    // 直接调用feign
    @GetMapping("/consumer/paymentSQL/{id}")
    public CommonResult<Payment> paymentSQL(@PathVariable("id") Long id{
        return paymentFeign.paymentSQL(id);
    }

这样接口报错(程序错误和控制台违规)就会调用PaymentFallback的降级熔断方法

测试84调用9003,此时故意关闭9003微服务提供者,看84消费侧自动降级,不会被耗死

框架比较:

SentinelHystrixresilience4j
隔离策略信号量隔离(并发线程数限流)线程池隔离/信号量隔离信号量隔离
熔断降级策略基于响应时间、异常比率、异常数基于异常比率基于异常比率、响应时间
实时统计实现滑动窗口(LeapArray)滑动窗口(基于RxJava)Ring Bit Buffer
动态规则配置支持多种数据源支持多种数据源有限支持
扩展性多个扩展性插件的形式接口的形式
基于注解的支持支持支持支持
限流基于QPS,支持基于调用关系的规范有限的支持Rate Limiter

规则持久化

不配每次重启微服务,之前在sentinel控制台上设置的规则就会不见。

一旦我们重启应用,sentinel规则消失,生产环境需要将配置规则进行持久化

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

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

spring:
  application:
    name: cloudalibaba-sentinel-service
  cloud:
    nacos:
      discovery:
        # Nacos服务注册中心地址
        server-addr: localhost:8848
    sentinel:
      transport:
        # sentinel dashboard 地址
        dashboard: 192.168.169.130:8080
        # 默认为8719,如果被占用会自动+1,直到找到为止
        port: 8719
      # 流控规则持久化到nacos
      datasource:
        dsl:
          nacos:
            server-addr: 192.168.169.130:8848
            data-id: ${spring.application.name}
            group-id: DEFAULT_GROUP
            data-type: json
            rule-type: flow
management:
  endpoints:
    web:
      exposure:
        include: "*"

添加Nacos业务规则配置:

{
    "resource": "/rateLimit/byUrl",
    "limitApp": "default",
    "grade": 1,
    "count": 1,
    "strategy": 0,
    "controlBehavior": 0,
    "clusterMode": false
}

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

在这里插入图片描述

重启后,访问微服务任一接口就出现原来的配置,持久化,可以说是初始化,之后可以改,一直存在nacos。

在这里插入图片描述

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

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

相关文章

为什么追踪员工的时间和出勤率很重要

监控员工的出勤时间和出勤率对于提高业务绩效和生产力至关重要。实施有效计时策略的组织可以帮助员工跟上项目目标和截止日期&#xff0c;提高客户满意度&#xff0c;并加强对员工产出的整体意识。所以每个企业组织都应该掌握员工出勤时间和出勤数据。 为什么要掌握员工出勤时…

阿里架构师耗时 1 年,把 P8 所需要的整个 Java 体系,都整理到了一起

有人调侃我们说&#xff1a; 程序员不如送外卖。送外卖是搬运食物&#xff0c;自己是搬运代码&#xff0c;都不产出新的东西…… 透支体力&#xff0c;又消耗健康&#xff0c;可替代性极强&#xff0c;30 岁之后就要面临被优化的危险…… 想跳槽&#xff0c;但是更高的平台难…

微信小程序怎么弄?【小程序制作】

微信小程序怎么弄&#xff1f;很多人都会想弄一个微信小程序&#xff0c;因为微信小程序这个轻应用现在的使用频率已经赶上微信了&#xff0c;有如此大的用户群体&#xff0c;企业和商家当然都想在这个庞大流量池里分一杯羹。那么微信小程序怎么弄呢&#xff1f;下面一起来看看…

群签名、环签名、盲签名

文章目录群签名定义安全性构造环签名定义安全性构造盲签名定义安全性构造群签名 定义 群签名方案是算法组 ΠGS(Gen,Sign,Ver,Open)\Pi_{GS}(Gen, Sign, Ver, Open)ΠGS​(Gen,Sign,Ver,Open)&#xff0c; Gen(1λ,n)Gen(1^\lambda,n)Gen(1λ,n)&#xff1a;密钥生成算法&…

百度Q3财报显AI技术厚度,“慢生意”稳步驶入“快车道”

一周前&#xff0c;笔者参加了一场百度主办的关于AIGC话题的沙龙&#xff0c;因为话题无比火爆&#xff0c;活动延迟到了一点钟才结束&#xff0c;以至于让约定的好友饭局也一等再等。 倒没有丝毫抱怨的意思&#xff0c;正是这个烧脑的活动&#xff0c;让我感受并体验到了当下最…

GEE开发之Modis_LAI数据分析和获取

GEE开发之Modis_LAI数据分析和获取1.遥感卫星数据叶面积指数LAI2.MOD15A2H(500m/8天)2.1 MOD15A2H下的指数2.2 LAI遥感影像查看获取3.LAI日数据下载4.LAI月数据下载5.LAI年数据下载前言&#xff1a;主要介绍LAI的概念&#xff0c;以及GEE下如何获取查看Modis下的LAI指数&#x…

Talk | 清华大学陈晓宇苏黎世联邦理工黄嘉伟 :基于实际应用的强化学习

本期为TechBeat人工智能社区第455期线上Talk&#xff01; 北京时间11月17日(周四)20:00&#xff0c;清华大学交叉信息研究院在读博士生——陈晓宇与苏黎世联邦理工大学计算机科学在读博士生——黄嘉伟的Talk将准时在TechBeat人工智能社区开播&#xff01; 他们与大家分享的主题…

NC发布猕猴大脑皮层多组学细胞图谱,助力神经系统疾病研究 | 时空专辑数据库

近日&#xff0c;杭州华大生命科学研究院&#xff08;以下简称杭州华大&#xff09;联合昆明理工大学灵长类转化医学研究院、美国艾伦脑科学研究所等国内外多家单位在国际学术期刊《自然通讯》&#xff08;Nature Communications&#xff09;在线发表题为《成年猕猴大脑皮层空间…

PyTorch深度学习实践——线性模型、梯度下降算法、反向传播

1、线性回归 参考资料1&#xff1a;https://blog.csdn.net/bit452/article/details/109627469 参考资料2&#xff1a;http://biranda.top/Pytorch%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0003%E2%80%94%E2%80%94%E7%BA%BF%E6%80%A7%E6%A8%A1%E5%9E%8B/#%E7%BA%BF%E6%80%A7%E6%A8%…

PC_多处理器

文章目录多处理器单指令单数据流SISD结构单指令流多数据流SIMD结构向量处理器多指令流单数据流MISD结构多指令多数据流MIMD结构小结硬件多线程细粒度多线程粗粒度多线程同时多线程多核处理器共享内存多处理器多处理器 常规的单处理器属于SISD常规多处理器属于MIMD 单指令单数…

腾格尔十月天传媒联手《巴林塔娜》,2255万粉丝多少买票支持

曾几何时&#xff0c;木桶原理非常流行&#xff0c;意思就是一个木桶能够盛多少水&#xff0c;取决于最短一块板的长度。可是随着社会的发展&#xff0c;木桶原理已经被淘汰&#xff0c;只要你拥有了团队合作&#xff0c;就可以统协作取长补短。 就拿有着“草原歌神”之称的腾格…

你的知识库能提高工作效率的7个原因

知识就是力量。但到目前为止&#xff0c;光有知识是不够的——你使用这些信息的方式让你领先于竞争对手。如果使用正确&#xff0c;知识库软件可以帮助您提供更好的服务&#xff0c;培训您的员工&#xff0c;并成为您的行业权威。拥有一个有效的知识库不仅会影响你在内部开展业…

Android assets

1.应用程序资源管理器assets assets就是apk工程中的一个普通目录&#xff0c;在每个工程的根目录下都可以发现(或者可以自己创建)一个assets目录。 assets目录用于专门保存各种外部文件&#xff0c;比如图像、音视频、配置文件、字体、自带数据库等。它之所以适合用来管理这些…

数据库mysql操作语言, DDL,DML,DQL

文章目录一. 数据库1. 数据库基本概念2. 数据库管理系统3. 数据库与表的概念二. 连接数据库的方式三. 如何操作DBMSSQL语句分类1. DDL 数据定义语言查看DBMS中已有的数据库数据库相关操作新建一个数据库查看数据库信息删除数据库使用一个数据库(切换一个数据库)表相关操作创建表…

HOOPS/MVO技术概述

更多参见&#xff1a;HOOPS学习笔记 MVO 1.引言 HOOPS/MVO是一个C类库&#xff0c;位于HOOPS 3D图形系统&#xff08;HOOPS/3DGS&#xff09;之上。它有一个模型/视图/操作员架构&#xff0c;封装了各种HOOPS/3DGS数据结构和概念&#xff0c;并提供了一系列通用应用程序级逻辑…

【无人机】基于粒子群优化干扰受限下无人机群辅助网络附matlab代码

✅作者简介&#xff1a;热爱科研的Matlab仿真开发者&#xff0c;修心和技术同步精进&#xff0c;matlab项目合作可私信。 &#x1f34e;个人主页&#xff1a;Matlab科研工作室 &#x1f34a;个人信条&#xff1a;格物致知。 更多Matlab仿真内容点击&#x1f447; 智能优化算法 …

Xception --tensorflow2.x

简介 Xception和SqueezeNet一样&#xff0c;是一种降低参数量的轻量级神经网络&#xff0c;它主要使用了 深度分离卷积&#xff08;Depthwise separable convolution)结构&#xff0c;该结构替换了原来的Inception中的多尺寸卷积结构。这里需要弄清深度分离卷积&#xff08;D…

【创建型设计模式-单例模式】一文搞懂单例模式的使用场景及代码实现的7种方式

1.什么是单例模式 在了解单例模式前&#xff0c;我们先来看一下它的定义&#xff1a; 确保一个类只有一个实例&#xff0c;而且自行实例化并且自行向整个系统提供这个实例&#xff0c;这个类称为单例类&#xff0c;它提供全局访问的方法&#xff0c; 单例模式是一种对象的创建型…

微型计算机原理速通期末复习

文章目录微机基础原码、反码、补码、移码溢出实数型功能结构8086/8088内部结构80286内部结构80386/80486内部结构标志寄存器FLAGS寄存器阵列段寄存器寻址标志寄存器EFLAGS分段结构数据寻址方式立即寻址直接寻址寄存器寻址寄存器间接寻址寄存器相对寻址基址-变址寻址基址-变址-相…

Solidity vs. Vyper:不同的智能合约语言的优缺点

本文探讨以下问题&#xff1a;哪种智能合约语言更有优势&#xff0c;Solidity 还是 Vyper&#xff1f;最近&#xff0c;关于哪种是“最好的”智能合约语言存在很多争论&#xff0c;当然了&#xff0c;每一种语言都有它的支持者。 这篇文章是为了回答这场辩论最根本的问题&…