SpringCloud Sentinel实战限流熔断降级应用

news2024/11/20 4:36:56

目录

  • 1 Sentinel核心库
    • 1.1 Sentinel介绍
    • 1.2 Sentinel核心功能
      • 1.2.1 流量控制
      • 1.2.2 熔断降级
    • 2 Sentinel 限流熔断降级
      • 2.1 @SentinelResource定义资源
      • 2.2 Sentinel的规则
        • 2.2.1 流量控制规则 (FlowRule)
        • 2.2.2 熔断降级规则 (DegradeRule)
        • 2.2.3 系统保护规则 (SystemRule)
        • 2.2.4 访问控制规则 (AuthorityRule)
        • 2.2.5 热点规则 (ParamFlowRule)
      • 2.3 OpenFeign支持
        • 2.3.1 fallback
        • 2.3.2 fallbackFactory


1 Sentinel核心库

Sentinel主页 https://github.com/alibaba/Sentinel/wiki/%E4%B8%BB%E9%A1%B5

1.1 Sentinel介绍

在这里插入图片描述

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

1)Sentinel核心组件

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

2:控制台(Dashboard):控制台主要负责管理推送规则、监控、集群限流分配管理、机器发现等。

2)同组件功能对比

参看:https://github.com/alibaba/Sentinel/wiki/%E5%9C%A8%E7%94%9F%E4%BA%A7%E7%8E%AF%E5%A2%83%E4%B8%AD%E4%BD%BF%E7%94%A8-Sentinel#%E5%90%8C%E7%B1%BB%E7%BB%84%E4%BB%B6%E5%8A%9F%E8%83%BD%E5%AF%B9%E6%AF%94

对比内容SentinelHystrix
隔离策略信号量隔离线程池隔离/信号量隔离
熔断降级策略基于响应时间或失败比率基于失败比率
实时指标实现滑动窗口滑动窗口(基于 RxJava)
规则配置支持多种数据源支持多种数据源
扩展性多个扩展点插件的形式
基于注解的支持支持支持
限流基于 QPS,支持基于调用关系的限流不支持
流量整形支持慢启动、匀速器模式不支持
系统负载保护支持不支持
控制台开箱即用,可配置规则、查看秒级监控、机器发现等不完善
常见框架的适配Servlet、Spring Cloud、Dubbo、gRPC 等Servlet、Spring Cloud Netflix

3)Sentinel基本概念

  • 资源

资源是 Sentinel 的关键概念。它可以是 Java 应用程序中的任何内容,例如,由应用程序提供的服务,或由应用程序调用的其它应用提供的服务,甚至可以是一段代码。

只要通过 Sentinel API 定义的代码,就是资源,能够被 Sentinel 保护起来。大部分情况下,可以使用方法签名,URL,甚至服务名称作为资源名来标示资源。

  • 规则

围绕资源的实时状态设定的规则,可以包括流量控制规则、熔断降级规则以及系统保护规则。所有规则可以动态实时调整。

1.2 Sentinel核心功能

1.2.1 流量控制

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

在这里插入图片描述

流量控制有以下几个角度:

  • 资源的调用关系,例如资源的调用链路,资源和资源之间的关系;
  • 运行指标,例如 QPS、线程池、系统负载等;
  • 控制的效果,例如直接限流、冷启动、排队等。

Sentinel 的设计理念是让您自由选择控制的角度,并进行灵活组合,从而达到想要的效果。

1.2.2 熔断降级

1)什么是熔断降级

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

在这里插入图片描述

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

2)Sentinel熔断降级设计

Hystrix 通过 线程池隔离 的方式,来对依赖(在 Sentinel 的概念中对应 资源)进行了隔离。这样做的好处是资源和资源之间做到了最彻底的隔离。缺点是除了增加了线程切换的成本(过多的线程池导致线程数目过多),还需要预先给各个资源做线程池大小的分配。

Sentinel熔断降级设计:

并发线程数限制:和资源池隔离的方法不同,Sentinel 通过限制资源并发线程的数量,来减少不稳定资源对其它资源的影响。这样不但没有线程切换的损耗,也不需要您预先分配线程池的大小。当某个资源出现不稳定的情况下,例如响应时间变长,对资源的直接影响就是会造成线程数的逐步堆积。当线程数在特定资源上堆积到一定的数量之后,对该资源的新请求就会被拒绝。堆积的线程完成任务后才开始继续接收请求。

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

3)系统自适应保护

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

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

2 Sentinel 限流熔断降级

Sentinel 可以简单的分为 Sentinel 核心库和 Dashboard。核心库不依赖 Dashboard,但是结合 Dashboard 可以取得最好的效果。我们先来学习Sentinel 核心库的使用,后面再学习Dashboard使用。

在这里插入图片描述

在我们项目中,用户请求通过hailtaxi-gateway路由到hailtaxi-driver或者hailtaxi-order,还有可能在hailtaxi-order中使用feign调用hailtaxi-driver,所以我们有可能在单个服务中实现熔断限流,也有可能要集成feign调用实现熔断限流,还有可能在微服务网关中实现熔断限流。我们接下来一步一步实现每一种熔断限流操作。

使用Spring Cloud Alibaba Sentinel

使用手册:https://github.com/alibaba/spring-cloud-alibaba/wiki/Sentinel

首先需要引入spring-cloud-starter-alibaba-sentinel依赖,并使用@SentinelResource标识资源。

hailtaxi-driver工程中引入spring-cloud-starter-alibaba-sentinel依赖,依赖如下:

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

2.1 @SentinelResource定义资源

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

具体参考:https://github.com/alibaba/Sentinel/wiki/%E6%B3%A8%E8%A7%A3%E6%94%AF%E6%8C%81

value资源名称,必需项(不能为空)
blockHandler / blockHandlerClassblockHandler 对应处理 BlockException 的函数名称,可选项。 ♞ blockHandler 函数访问范围需要是 public; ♞ 返回类型需要与原方法相匹配,参数类型需要和原方法相匹配并且最后加一个额外的参数,类型为 BlockException。 ♞ blockHandler 函数默认需要和原方法在同一个类中。若希望使用其他类的函数,则可以指定 blockHandlerClass 为对应的类的 Class 对象,注意对应的函数必需为 static 函数,否则无法解析。
fallback / fallbackClassfallback 函数名称,可选项,用于在抛出异常的时候提供 fallback 处理逻辑。fallback 函数可以针对所有类型的异常(除了 exceptionsToIgnore 里面排除掉的异常类型)进行处理。fallback 函数签名和位置要求: ♞ 返回值类型必须与原函数返回值类型一致; ♞ 方法参数列表需要和原函数一致,或者可以额外多一个 Throwable 类型的参数用于接收对应的异常。 ♞ fallback 函数默认需要和原方法在同一个类中。若希望使用其他类的函数,则可以指定 fallbackClass 为对应的类的 Class 对象,注意对应的函数必需为 static 函数,否则无法解析。
defaultFallback(1.6.0 开始)默认的 fallback 函数名称,可选项,通常用于通用的 fallback 逻辑(即可以用于很多服务或方法)。默认 fallback 函数可以针对所有类型的异常(除了 exceptionsToIgnore 里面排除掉的异常类型)进行处理。若同时配置了 fallback 和 defaultFallback,则只有 fallback 会生效。defaultFallback 函数签名要求: ♞ 返回值类型必须与原函数返回值类型一致; ♞ 方法参数列表需要为空,或者可以额外多一个 Throwable 类型的参数用于接收对应的异常。 ♞ defaultFallback 函数默认需要和原方法在同一个类中。若希望使用其他类的函数,则可以指定 fallbackClass 为对应的类的 Class 对象,注意对应的函数必需为 static 函数,否则无法解析。
exceptionsToIgnore(1.6.0 开始)用于指定哪些异常被排除掉,不会计入异常统计中,也不会进入 fallback 逻辑中,而是会原样抛出。
entryTypeentry 类型,可选项(默认为 EntryType.OUT)

blockHandler/blockHandlerClass

hailtaxi-driver中找到DriverController中的info方法,用户在打车的时候,会查询司机信息,如果司机不存在,此时会报错,代码改造如下:

/****
     * 司机信息
     */
@GetMapping(value = "/info/{id}")
//@RequestMapping(value = "/info/{id}")
public Driver info(@PathVariable(value = "id")String id){ // 去掉参数上的HttpServletRequest request
    log.info("当前服务占用的端口为:{}",port);
    Driver driver = driverService.findById(id);
    if (driver==null) {
        throw new RuntimeException("司机id="+id+"不存在");
    }
    return driver;
}

如果此时访问:http://localhost:18081/driver/info/3 查询司机信息,如果没有ID为3的司机信息,会报如下错误,

在这里插入图片描述

这种体验非常差,我们可以集成Sentinel使用@SentinelResourceblockHandler返回默认错误信息,形成降级!!!

1、Sentinel 支持在程序中抛出它定义的BlockException异常,该异常会被Sentinel捕获,然后走降级方法,

info()方法添加一个@SentinelResource注解,用来标注资源,表示当前方法需要执行限流、降级,在注解中添加value属性,用来标注资源,说白了就是给当前资源起个名字,blockHandler用来表示当前方法发生BlockException异常的时候,将处理流程交给指定的方法blockExHandler()处理,此时blockExHandler()方法必须和抛出异常的方法在同一个类中,这是一种降级操作,代码如下:

/****
     * 司机信息
     */
@SentinelResource(value = "info",blockHandler = "blockExHandler")
@RequestMapping(value = "/info/{id}")
public Driver info(@PathVariable(value = "id")String id) throws BlockException {
    log.info("当前服务占用的端口为:{}",port);
    Driver driver = driverService.findById(id);
    if (driver==null) {
        //throw new RuntimeException("司机id="+id+"不存在");
        throw new SystemBlockException("info", "司机id="+id+"不存在",null); // 抛出BlockException
    }
    return driver;
}

/**
     * info资源出现BlockException后的降级处理
     */
public Driver blockExHandler(String id,BlockException e) {
    Driver driver = new Driver();
    driver.setId(id);
    driver.setName("系统繁忙,稍后再试");
    return driver;
}

在这里插入图片描述

注意:

如果blockHandler方法和资源方法不在同一个类中,我们可以在@SentinelResource中添加blockHandlerClass属性,指定降级处理类的方法所在的类,且要求blockHandler方法是静态的,代码如下:

@SentinelResource(value = "info",blockHandler = "blockExHandler",blockHandlerClass = "xxx.xxx.Xxxx")

2、启动测试,访问:http://localhost:18081/driver/info/3 测试出错效果如下:

在这里插入图片描述

fallback/fallbackClass

1、如果我们希望抛出任何异常都能处理,都能调用默认处理方法,而并非只是BlockException异常才调用,此时可以使用@SentinelResourcefallback属性,代码如下:

/****
     * 司机信息
     */
@SentinelResource(value = "info"/*,blockHandler = "blockExHandler"*/,fallback = "exHandler")
@RequestMapping(value = "/info/{id}")
public Driver info(@PathVariable(value = "id")String id) throws BlockException {
    log.info("当前服务占用的端口为:{}",port);
    Driver driver = driverService.findById(id);
    if (driver==null) {
        throw new RuntimeException("司机id="+id+"不存在");
        // throw new SystemBlockException("info", "司机id="+id+"不存在",null); // 抛出BlockException
    }
    return driver;
}
/**
     * info资源出现任何类型异常后的降级处理
     * 方法参数可以添加一个Throwable 类型的参数,也可不添加
     */
public Driver exHandler(String id,Throwable e) {
    Driver driver = new Driver();
    driver.setId(id);
    driver.setName("系统繁忙,稍后再试");
    return driver;
}

注意:

如果fallback方法和当前的资源方法不在同一个类中,可以使用@SentinelResource注解的fallbackClass实现,也要求fallback方法是静态的,代码如下:

@SentinelResource(value = "info",fallback ="exHandler" ,fallbackClass = "xx.xxx.xxx.xx.Xxx")

2、访问 http://localhost:18081/driver/info/3 测试出错效果如下:

在这里插入图片描述

defaultFallback

上面无论是blockHandler还是fallback,每个方法发生异常,都要为方法独立创建一个处理异常的方法,效率非常低,我们可以使用@SentinelResource注解的defaultFallback属性,为一个类指定一个全局的处理错误的方法,代码如下:

@RestController
@RequestMapping(value = "/driver")
@Slf4j
@RefreshScope
@SentinelResource(defaultFallback = "defaultExHandler")
public class DriverController {
    @Autowired
    private DriverService driverService;

    public Driver defaultExHandler(Throwable e) {
        Driver driver = new Driver();
        driver.setName("系统繁忙,稍后再试");
        return driver;
    }

    /****
     * 司机信息
     */
    //@SentinelResource(value = "info"/*,blockHandler = "blockExHandler"*/,fallback = "exHandler")
    @SentinelResource("info")
    @RequestMapping(value = "/info/{id}")
    public Driver info(@PathVariable(value = "id")String id) throws BlockException {
        log.info("当前服务占用的端口为:{}",port);
        Driver driver = driverService.findById(id);
        if (driver==null) {
            throw new RuntimeException("司机id="+id+"不存在");
            // throw new SystemBlockException("info", "司机id="+id+"不存在",null); // 抛出BlockException
        }
        return driver;
    }

访问 http://localhost:18081/driver/info/3 效果如下:

在这里插入图片描述

2.2 Sentinel的规则

查看:https://github.com/alibaba/Sentinel/wiki/%E5%A6%82%E4%BD%95%E4%BD%BF%E7%94%A8#%E8%A7%84%E5%88%99%E7%9A%84%E7%A7%8D%E7%B1%BB

Sentinel 的所有规则都可以在内存态中动态地查询及修改,修改之后立即生效。同时 Sentinel 也提供相关 API,供您来定制自己的规则策略。

Sentinel 支持以下几种规则:流量控制规则熔断降级规则系统保护规则来源访问控制规则热点参数规则

2.2.1 流量控制规则 (FlowRule)

查看:

https://github.com/alibaba/Sentinel/wiki/%E5%A6%82%E4%BD%95%E4%BD%BF%E7%94%A8#%E6%B5%81%E9%87%8F%E6%8E%A7%E5%88%B6%E8%A7%84%E5%88%99-flowrule

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

流量规则的定义,重要属性如下:

Field说明默认值
resource资源名,资源名是限流规则的作用对象
count限流阈值
grade限流阈值类型,QPS 模式(1)或并发线程数模式(0)QPS 模式
limitApp流控针对的调用来源default,代表不区分调用来源
strategy调用关系限流策略:直接、链路、关联根据资源本身(直接)
controlBehavior流控效果(直接拒绝/WarmUp/匀速+排队等待),不支持按调用关系限流直接拒绝
clusterMode是否集群限流

同一个资源可以同时有多个限流规则,检查规则时会依次检查

strategy限流策略说明:

直接:资源达到限流条件时,直接限流。

关联:A资源关联B资源,当关联的B资源达到阈值限流时,A资源也会被限流。

链路:对于某资源C,有两个入口,从资源A->C,从资源B->C, 通过指定入口资源可以达到只记录从该入口进来的流量(指定资源从入口资源进来的流量,如果达到阈值,就可以对其限流)。

controlBehavior流控说明:

直接拒绝:请求直接失败。
WarmUp:当系统长期处于低水位的情况下,当流量突然增加时,直接把系统拉升到高水位可能瞬间把系统压垮。通过"冷启动",让通过的流量缓慢增加,在一定时间内逐渐增加到阈值上限,给冷系统一个预热的时间,避免冷系统被压垮。
排队等待:排队处理请求。

理解上面规则的定义之后,我们可以通过调用 FlowRuleManager.loadRules() 方法来用硬编码的方式定义流量控制规则。

QPS流量控制

1、我们先实现基于QPS流量控制,在hailtaxi-driverDriverApplication启动类上添加如下方法加载限流规则,当DriverApplication初始化完成之后加载规则,代码如下:

/***
 * 初始化规则
 */
@PostConstruct
private void initFlowRule() {
    //规则集合
    List<FlowRule> rules = new ArrayList<FlowRule>();
    //定义一个规则
    FlowRule rule = new FlowRule("info");
    // 设置阈值
    rule.setCount(2);
    //设置限流阈值类型
    rule.setGrade(RuleConstant.FLOW_GRADE_QPS);
    //default,代表不区分调用来源
    rule.setLimitApp("default");
    //设置流控效果
    rule.setControlBehavior(RuleConstant.CONTROL_BEHAVIOR_DEFAULT);
    //将定义的规则添加到集合中
    rules.add(rule);
    //加载规则
    FlowRuleManager.loadRules(rules);
}

2、访问 http://localhost:18081/driver/info/1 此时不会抛出异常,但是频繁刷新,则会调用降级方法,效果如下:

在这里插入图片描述

线程数流量控制

1、我们修改限流阈值类型,代码如下:

@PostConstruct
public void initFlowRule() {
    //规则集合
    List<FlowRule> rules = new ArrayList<>();
    // 定义一个规则
    FlowRule rule = new FlowRule("info");
    // 设置基流量控制 的类型
    rule.setGrade(RuleConstant.FLOW_GRADE_THREAD);//默认是qps
    //设置流量阈值
    rule.setCount(2);
    // 将 规则添加到 集合中
    rules.add(rule);
    // 加载规则
    FlowRuleManager.loadRules(rules);
}

2、此时再来访问http://localhost:18081/driver/info/1我们发现用浏览器无论怎么访问都不会出现降级现象,但是如果用Jmeter模拟多个线程,效果就不一样了,效果如下:

在这里插入图片描述

2.2.2 熔断降级规则 (DegradeRule)

参看:

https://github.com/alibaba/Sentinel/wiki/%E5%A6%82%E4%BD%95%E4%BD%BF%E7%94%A8#%E7%86%94%E6%96%AD%E9%99%8D%E7%BA%A7%E8%A7%84%E5%88%99-degraderule

https://github.com/alibaba/Sentinel/wiki/%E7%86%94%E6%96%AD%E9%99%8D%E7%BA%A7

熔断降级规则包含下面几个重要的属性:

Field说明默认值
resource资源名,即规则的作用对象
grade熔断策略,支持慢调用比例/异常比例/异常数策略慢调用比例
count慢调用比例模式下为慢调用临界 RT(超出该值计为慢调用);异常比例/异常数模式下为对应的阈值
timeWindow熔断时长,单位为 s
minRequestAmount熔断触发的最小请求数,请求数小于该值时即使异常比率超出阈值也不会熔断(1.7.0 引入)5
statIntervalMs统计时长(单位为 ms),如 60*1000 代表分钟级(1.8.0 引入)1000 ms
slowRatioThreshold慢调用比例阈值,仅慢调用比例模式有效(1.8.0 引入)

同一个资源可以同时有多个降级规则。

理解上面规则的定义之后,我们可以通过调用 DegradeRuleManager.loadRules() 方法来用硬编码的方式定义熔断规则,

1、在DriverApplication规则定义如下:

@PostConstruct
public void initDegradeRule() {
    List<DegradeRule> rules = new ArrayList<>();
    DegradeRule rule = new DegradeRule();
    // 设置资源名称
    rule.setResource("info");
    /**
         * 设置熔断策略
         * DEGRADE_GRADE_RT:平均响应时间
         * DEGRADE_GRADE_EXCEPTION_RATIO:异常比例数量
         * DEGRADE_GRADE_EXCEPTION_COUNT:异常数
         */
    rule.setGrade(RuleConstant.DEGRADE_GRADE_EXCEPTION_COUNT);
    //设置阈值
    rule.setCount(2);
    //设置 熔断时长
    rule.setTimeWindow(30);
    // 统计时长(单位为 ms) 默认1000
    rule.setStatIntervalMs(60*1000);
    //将规则添加到集合中
    rules.add(rule);
    DegradeRuleManager.loadRules(rules);
}

2、首先访问:http://localhost:18081/driver/info/1 ,确保没问题,

其次访问:http://localhost:18081/driver/info/3,多访问几次,造成熔断(5+2=7)

然后在访问:http://localhost:18081/driver/info/1,会发现已经有熔断降级效果了,

且查看服务控制台,发现不会有信息输出,表明已经熔断了,且从页面展示效果来看走了降级

在这里插入图片描述

等待30s后,再次访问:http://localhost:18081/driver/info/1,查看熔断是否结束!

2.2.3 系统保护规则 (SystemRule)

参考:

https://github.com/alibaba/Sentinel/wiki/%E5%A6%82%E4%BD%95%E4%BD%BF%E7%94%A8#%E7%B3%BB%E7%BB%9F%E4%BF%9D%E6%8A%A4%E8%A7%84%E5%88%99-systemrule

https://github.com/alibaba/Sentinel/wiki/%E7%B3%BB%E7%BB%9F%E8%87%AA%E9%80%82%E5%BA%94%E9%99%90%E6%B5%81

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

系统规则包含下面几个重要的属性:

Field说明默认值
highestSystemLoadload1 触发值,用于触发自适应控制阶段-1 (不生效)
avgRt所有入口流量的平均响应时间-1 (不生效)
maxThread入口流量的最大并发数-1 (不生效)
qps所有入口资源的 QPS-1 (不生效)
highestCpuUsage当前系统的 CPU 使用率(0.0-1.0)-1 (不生效)

理解上面规则的定义之后,我们可以通过调用 SystemRuleManager.loadRules() 方法来用硬编码的方式定义流量控制规则。

1、在hailtaxi-driverDriverApplication中创建如下方法,代码如下:

/***
 * 系统自我保护
 */
@PostConstruct
private void initSystemRule() {
    //系统自我保护集合
    List<SystemRule> rules = new ArrayList<>();
    //创建系统自我保护规则
    SystemRule rule = new SystemRule();
    //CPU使用率 值为[0,1],-1 (不生效)
    rule.setHighestCpuUsage(0.2);
    //所有入口资源的 QPS,-1 (不生效)
    rule.setQps(10);
    //入口流量的最大并发数,-1 (不生效)
    rule.setMaxThread(5);
    //所有入口流量的平均响应时间,单位:毫秒,-1 (不生效)
    rule.setAvgRt(1000);
    //load1 触发值,用于触发自适应控制阶段,系统最高负载,建议取值 CPU cores * 2.5
    rule.setHighestSystemLoad(20);
    //将规则加入到集合
    rules.add(rule);
    SystemRuleManager.loadRules(rules);
}

我们可以测试CPU使用率自我保护,使用jmeter测试如下:

在这里插入图片描述

2.2.4 访问控制规则 (AuthorityRule)

参看:

https://github.com/alibaba/Sentinel/wiki/%E5%A6%82%E4%BD%95%E4%BD%BF%E7%94%A8#%E8%AE%BF%E9%97%AE%E6%8E%A7%E5%88%B6%E8%A7%84%E5%88%99-authorityrule

https://github.com/alibaba/Sentinel/wiki/%E9%BB%91%E7%99%BD%E5%90%8D%E5%8D%95%E6%8E%A7%E5%88%B6

很多时候,我们需要根据调用方来限制资源是否通过,这时候可以使用 Sentinel 的访问控制(黑白名单)的功能。黑白名单根据资源的请求来源(origin)限制资源是否通过,若配置白名单则只有请求来源位于白名单内时才可通过;若配置黑名单则请求来源位于黑名单时不通过,其余的请求通过。

授权规则,即黑白名单规则(AuthorityRule)非常简单,主要有以下配置项:

  • resource:资源名,即规则的作用对象
  • limitApp:对应的黑名单/白名单,不同 origin 用 , 分隔,如 appA,appB
  • strategy:限制模式,AUTHORITY_WHITE 为白名单模式,AUTHORITY_BLACK 为黑名单模式,默认为白名单模式

了解了以上规则后,可以通过AuthorityRuleManager.loadRules来加载规则

1、在hailtaxi-driverDriverApplication中创建如下方法,代码如下:

@PostConstruct
    public void initAuthorityRule() {
        AuthorityRule rule = new AuthorityRule();
        rule.setResource("info");
        rule.setStrategy(RuleConstant.AUTHORITY_WHITE);
        rule.setLimitApp("127.0.0.1,appB");
        AuthorityRuleManager.loadRules(Collections.singletonList(rule));
    }

    /**
     * Sentinel提供了 RequestOriginParser 接口来处理访问来源,Sentinel保护的资源如果被访问,
     * 就会调用 RequestOriginParser解析访问来源
     */
    @Component
    public class IpLimiter implements RequestOriginParser{

        @Override
        public String parseOrigin(HttpServletRequest httpServletRequest) {
            return httpServletRequest.getRemoteAddr();
        }
    }

2、访问:http://localhost:18081/driver/info/1,不通过,走了降级

  访问:http://127.0.0.1:18081/driver/info/1

2.2.5 热点规则 (ParamFlowRule)

参看:https://github.com/alibaba/Sentinel/wiki/%E5%A6%82%E4%BD%95%E4%BD%BF%E7%94%A8#%E7%83%AD%E7%82%B9%E8%A7%84%E5%88%99-paramflowrule

何为热点?热点即经常访问的数据。

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

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

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

在这里插入图片描述

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

要使用热点参数限流功能,需要引入以下依赖,将该依赖加入到hailtaxi-driver中:

<!--热点参数限流-->
<dependency>
    <groupId>com.alibaba.csp</groupId>
    <artifactId>sentinel-parameter-flow-control</artifactId>
    <version>1.8.1</version>
</dependency>

然后为对应的资源配置热点参数限流规则,并在 entry 的时候传入相应的参数,即可使热点参数限流生效。

热点参数规则(ParamFlowRule)类似于流量控制规则(FlowRule):

属性说明默认值
resource资源名,必填
count限流阈值,必填
grade限流模式QPS 模式
durationInSec统计窗口时间长度(单位为秒),1.6.0 版本开始支持1s
controlBehavior流控效果(支持快速失败和匀速排队模式),1.6.0 版本开始支持快速失败
maxQueueingTimeMs最大排队等待时长(仅在匀速排队模式生效),1.6.0 版本开始支持0ms
paramIdx热点参数的索引,必填,对应 SphU.entry(xxx, args) 中的参数索引位置
paramFlowItemList参数例外项,可以针对指定的参数值单独设置限流阈值,不受前面 count 阈值的限制。仅支持基本类型和字符串类型
clusterMode是否是集群参数流控规则false
clusterConfig集群流控相关配置

我们可以通过 ParamFlowRuleManagerloadRules 方法更新热点参数规则

1、在DriverController中创建一个司机筛选方法,比如根据城市来筛选,在DriverController中创建一个方法:

/***
     * 搜素指定城市的司机
     */
@SentinelResource(value = "search")
@GetMapping(value = "/search/{city}")
public Driver search(@PathVariable(value = "city")String city){
    System.out.println("查询的司机所在城市:"+city);
    //假设查询到了一个司机信息
    Driver driver = new Driver();
    driver.setName("唐僧老师");
    driver.setId("No.1");
    return driver;
}

2、对热门参数进行控制,对热点数据执行特殊限流,比如资源参数列表中的第一个参数值为恩施的时候执行限流,在DriverApplication中创建限流配置,代码如下:

/***
 * 热点参数初始化
 */
@PostConstruct
private static void initParamFlowRules() {
    ParamFlowRule rule = new ParamFlowRule("search")
            //参数下标为0
            .setParamIdx(0)
            //限流模式为QPS
            .setGrade(RuleConstant.FLOW_GRADE_QPS)
            //统计窗口时间长度(单位为秒)
            .setDurationInSec(10)
            //流控效果(支持快速失败和匀速排队模式)
            //CONTROL_BEHAVIOR_DEFAULT:限流行为,直接拒绝
            //CONTROL_BEHAVIOR_WARM_UP:限流行为,匀速排队
            //CONTROL_BEHAVIOR_RATE_LIMITER:限流行为,匀速排队
            .setControlBehavior(RuleConstant.CONTROL_BEHAVIOR_DEFAULT)
            //最大排队等待时长(仅在匀速排队模式生效  CONTROL_BEHAVIOR_RATE_LIMITER)
            //.setMaxQueueingTimeMs(600)
            //最大阈值为5
            .setCount(5);

    // 为特定参数单独设置规则
    //如下配置:当参数值为恩施的时候,阈值到达2的时候则执行限流
    ParamFlowItem item = new ParamFlowItem()
            //参数类型为String类型
            .setClassType(String.class.getName())
            //设置阈值为2
            .setCount(2)
            //需要统计的值
            .setObject(String.valueOf("恩施"));
    rule.setParamFlowItemList(Collections.singletonList(item)); //返回的是不可变的集合,但是这个长度的集合只有1,可以减少内存空间
    //加载热点数据
    ParamFlowRuleManager.loadRules(Collections.singletonList(rule));
}

2、我们访问 http://localhost:18081/driver/search/天津/ 的时候,连续执行5次,才会限流,

我们访问 http://localhost:18081/driver/search/恩施/ 的时候,连续执行2次,就会限流,

2.3 OpenFeign支持

参看:

https://github.com/alibaba/spring-cloud-alibaba/wiki/Sentinel#feign-%E6%94%AF%E6%8C%81

Sentinel 适配了 Feign 组件。如果想使用,除了外还需要 2 个步骤:

1:引入 `spring-cloud-starter-alibaba-sentinel` 的依赖
2:加入 spring-cloud-starter-openfeign 依赖
3:配置文件打开 Sentinel 对 Feign 的支持:feign.sentinel.enabled=true

在这里插入图片描述

在上面案例中,我们可以实现用户打车成功调用hailtaxi-order执行下单,并且通过feign调用hailtaxi-driver修改司机状态,此时我们可以使用Sentinel实现Feign调用降级、限流。

注意:

在进行操作之前,可以先将hailtaxi-driver中的访问控制规则注释掉,注释掉DriverApplication#initAuthorityRuleDriverApplication#initSystemRule上的注解即可

1、在hailtaxi-order中引入sentinelOpenFeign依赖,配置如下:

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

<!--Openfeign  api模块中已存在也可不引入-->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>

2、在hailtaxi-order的配置文件中开启Feign支持sentinel,配置如下:

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

注意:现在配置信息都存放在了nacos中,所以找到hailtaxi-order.yaml的Data ID配置,将以上配置添加进去!

在这里插入图片描述

注意修改完后一定要发布才能生效!!!

3、因为hailtaxi-order要通过openfeign去调用hailtaxi-driver中的DriverController#status方法,改造一下该方法

/****
     * 更新司机信息
     */
@PutMapping(value = "/status/{id}/{status}")
public Driver status(@PathVariable(value = "id")String id,@PathVariable(value = "status")Integer status) throws Exception {
    log.info("当前服务占用的端口为:{}",port);
    //修改状态
    driverService.update(id,status);
    //修改状态后的司机信息
    Driver driver = driverService.findById(id);
    if (driver == null) {
        throw new RuntimeException("学生id="+id+",不存在");
    }
    return driver;
}

模拟被调用服务出现异常的情况

3、先验证正确性,启动hailtaxi-gatewayhailtaxi-orderhailtaxi-driver服务,使用postman访问:

http://localhost:8001/order

4、为了测试程序异常能实现降级操作,我们在hailtaxi-order中将OrderInfoController.add()方法的司机ID改成一个不存在的司机ID,让程序报错,测试降级处理,代码如下:

/***
     * 下单
     */
@PostMapping
public OrderInfo add(){
    //修改司机信息  司机ID=1
    Driver driver = driverFeign.status("3",2);// 改成一个不存在的id
    //创建订单
    OrderInfo orderInfo = new OrderInfo("No"+((int)(Math.random()*10000)), (int)(Math.random()*100), new Date(), "深圳北站", "罗湖港", driver);
    orderInfoService.add(orderInfo);
    return orderInfo;
}

在这里插入图片描述

5、再次启动测试:

在这里插入图片描述

注意:服务调用出错要进行降级操作有两个地方都可以进行,一是在被调用方进行降级,一是在调用方进行降级,

在被调用方进行降级前面已经讲过了,所以接下来讲解如何在调用方(openfeign)结合 sentinel 进行降级处理。

2.3.1 fallback

hailtaxi-api模块中找到DriverFeign接口:

1、为Feign接口创建一个实现类:com.itheima.driver.feign.fallback.DriverFeignFallback,在实现类中处理程序异常降级处理方法,代码如下:

package com.itheima.driver.feign.fallback;

import com.itheima.driver.feign.DriverFeign;
import com.itheima.driver.model.Driver;
import org.springframework.stereotype.Component;

@Component
public class DriverFeignFallback implements DriverFeign {

    /**
     * status()降级处理方法
     */
    @Override
    public Driver status(String id, Integer status) {
        Driver driver = new Driver();
        driver.setId(id);
        driver.setStatus(status);
        driver.setName("系统比较繁忙,请您稍后再试!");
        return driver;
    }
}

2、在DriverFeign接口上添加fallback属性指定降级处理的类,代码如下:

@FeignClient(value = "hailtaxi-driver",fallback = DriverFeignFallback.class)

注意:修改了hailtaxi-api模块,最好cleanpackage!!

3、启动运行,会发生如下问题:

java.lang.AbstractMethodError: com.alibaba.cloud.sentinel.feign.SentinelContractHolder.parseAndValidatateMetadata(Ljava/lang/Class;)Ljava/util/List;

出现上面问题的主要原因是当前SpringCloud版本存在问题。

Hoxton.SR1` 中,`fegin.context`接口方法的定义为`parseAndValidatateMetadata` `Hoxton.SR3` 中,`fegin.context`接口方法的定义为`parseAndValidateMetadata

我们现在需要把Hoxton.SR1换成Hoxton.SR3,因此需要在hailtaxi-parent修改SpringCloud版本:

在这里插入图片描述

SpringCloud版本升级至Hoxton.SR3同时将SpringBoot版本升级至2.2.10,如上图:

https://github.com/spring-cloud/spring-cloud-release/wiki/Spring-Cloud-Hoxton-Release-Notes

此时我们测试,效果如下:

在这里插入图片描述

2.3.2 fallbackFactory

我们可以为DriverFeign接口创建一个降级处理的工厂对象,在工厂对象中处理程序异常降级处理方法,用工厂对象处理可以拿到异常信息,代码如下:

1、创建:com.itheima.driver.feign.fallback.DriverFeignFallbackFactory

package com.itheima.driver.feign.fallback;

import com.itheima.driver.feign.DriverFeign;
import com.itheima.driver.model.Driver;
import feign.hystrix.FallbackFactory;
import org.springframework.stereotype.Component;

@Component
public class DriverFeignFallbackFactory implements FallbackFactory<DriverFeign> {
    @Override
    public DriverFeign create(Throwable throwable) {
        return new DriverFeign() {
            /**
             * status()降级处理方法
             */
            @Override
            public Driver status(String id, Integer status) {
                Driver driver = new Driver();
                driver.setId(id);
                driver.setStatus(status);
                driver.setName("系统比较繁忙,请您稍后再试!");
                return driver;
            }
        };
    }
}

2、在DriverFeign接口上添加fallbackFactory属性指定讲解处理的类,代码如下:

@FeignClient(value = "hailtaxi-driver",fallbackFactory = DriverFeignFallbackFactory.class)

3、再次启动测试,效果如下:

在这里插入图片描述

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

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

相关文章

Tomcat配置https协议证书-阿里云,Nginx配置https协议证书-阿里云,Tomcat配置https证书pfx转jks

Tomcat/Nginx配置https协议证书 前言Tomcat配置https协议证书-阿里云方式一 pfx配置证书重启即可 方式二 jkspfx生成jks配置证书重启即可 Nginx配置https协议证书-阿里云实现方式重启即可 其他Tomcat相关配置例子如下nginx配置相关例子如下 前言 阿里云官网&#xff1a;https:…

探索Java面向对象编程的奇妙世界(三)

⭐ 垃圾回收机制(Garbage Collection)⭐ JVM 调优和 Full GC⭐ this 关键字⭐ static 关键字 ⭐ 垃圾回收机制(Garbage Collection) Java 引入了垃圾回收机制&#xff0c;令 C程序员最头疼的内存管理问题迎刃而解。Java 程序员可以将更多的精力放到业务逻辑上而不是内存管理工…

网安面试只要掌握这十点技巧,绝对轻轻松松吊打面试官

结合工作经验&#xff0c;在这里笔者给企业网管员提供一些保障企业网络安全的建议&#xff0c;帮助他们用以抵御网络入侵、恶意软件和垃圾邮件。 定义用户完成相关任务的恰当权限 拥有管理员权限的用户也就拥有执行破坏系统的活动能力&#xff0c;例如&#xff1a; ・偶然对系…

挂耳式耳机品牌排行榜,良心推荐这四款蓝牙耳机

在蓝牙耳机越来越普及的同时&#xff0c;大家开始重视佩戴耳机时的舒适度&#xff0c;市面上的耳机形式也逐步迭代&#xff0c;目前流行的开放式耳机不仅很好的避免长期佩戴耳机产生的酸痛感&#xff0c;而且对耳道健康问题的处理也具有极佳的效果。那么&#xff0c;面对市面上…

VB显示“shell32.dll”中的图标

在Form上添加一个ListBox列表控件 代码如下&#xff1a; Option Explicit Private Declare Function ExtractIconEx Lib “shell32.dll” Alias “ExtractIconExA” (ByVal lpszFile As String, ByVal nIconIndex As Long, phiconLarge As Long, phiconSmall As Long, ByVal nI…

奇偶分频电路

目录 偶数分频 寄存器级联法 计数器法 奇数分频 不满足50%占空比 50%占空比 偶数分频 寄存器级联法 寄存器级联法能实现2^N的偶数分频&#xff0c;具体做法是采用寄存器结构的电路&#xff0c;每当时钟上升沿到来的时候对输出结果进行翻转&#xff0c;以此来实现偶数分…

拥有自我意识的AI:AutoGPT | 得物技术

1.引言 ChatGPT在当下已经风靡一时&#xff0c;作为自然语言处理模型的佼佼者&#xff0c;ChatGPT的优势在于其能够生成流畅、连贯的对话&#xff0c;同时还能够理解上下文并根据上下文进行回答。针对不同的应用场景可以进行快速定制&#xff0c;例如&#xff0c;在客服、教育…

【Unity-UGUI控件全面解析】| TextMeshPro 控件详解

🎬【Unity-UGUI控件全面解析】| TextMeshPro控件详解一、组件介绍二、组件属性面板三、代码操作组件四、组件常用方法示例4.1 Font Asset Creator 面板介绍4.2 制作中文字体库五、组件相关扩展使用5.1 软化/扩张 效果5.2 描边效果5.3 投影效果5.4 光照效果5.5 外发光效果💯…

5.25 费解的开关

思路&#xff0c;枚举 开关按下两次就复原&#xff0c;所以一个开关只有两种情况&#xff0c;按下和不按下&#xff0c;5*5的开关&#xff0c;一共25个开关&#xff0c;一共有2^25种情况&#xff0c;for (int i 0;i < 2^25;i)进行操作&#xff0c;计算按下开关次数&#x…

mac 安装 MongoDB

一.官网下载安装包 1.1 下载安装包 Download MongoDB Community Server | MongoDB 1.2 将下载好的 MongoDB 安装包解压缩&#xff0c;并将文件夹名改为 mongodb&#xff08;可改成自己想要的任何名字&#xff09;。 1.3 按快捷键 Command Shift G 打开前往文件夹弹窗&#…

没有经验能做产品经理吗?

没有经验能做产品经理吗&#xff1f;这是一个经常被讨论的问题&#xff0c;因为很多人想转行成为产品经理&#xff0c;但他们没有相关的工作经验。这里我也给出一些解答。 一、产品经理的职责和技能 首先&#xff0c;让我们看一下产品经理的职责和技能。产品经理是负责产品开…

LeetCode:相交链表(java)

相交链表 题目描述指针法解题 #LeetCode 160题&#xff1a;相交链表&#xff0c;原题链接 原题链接。相交链表–可以打开测试 题目描述 给你两个单链表的头节点 headA 和 headB &#xff0c;请你找出并返回两个单链表相交的起始节点。如果两个链表不存在相交节点&#xff0c;返…

【Python Power BI】零基础也能轻松掌握的学习路线与参考资料

Python和Power BI是现代数据分析和可视化领域中最受欢迎的工具之一&#xff0c;Python是一种高级编程语言&#xff0c;广泛用于数据科学和分析&#xff0c;而Power BI是一种业务智能工具&#xff0c;用于创建交互式大屏幕和实时报表。Python和Power BI的结合使用可以为数据科学…

【布隆过滤器】BitMap与布隆过滤器

1.案例&#xff1a;40亿个QQ号&#xff0c;限制1G内存&#xff0c;如何去重&#xff1f; 40亿个unsigned int&#xff0c;如果直接用内存存储的话&#xff0c;需要&#xff1a; 4*4000000000 /1024/1024/1024 14.9G &#xff0c;考虑到其中有一些重复的话&#xff0c;那1G的…

【P31】JMeter 循环控制器(Loop Controller)

这文章目录 一、循环控制器&#xff08;Loop Controller&#xff09;参数说明二、测试计划设计2.1、设置循环次数2.2、勾选永远2.3、设置线程组的持续时间 一、循环控制器&#xff08;Loop Controller&#xff09;参数说明 可以对部分逻辑按常量进行循环迭代 选择线程组右键 …

探索编程的极限:挑战炫技代码

程序员常常被视为具有超强技术能力的人才&#xff0c;而他们手中的代码也往往充满了令普通人惊叹的炫技操作。作为程序员的我&#xff0c;将和大家分享一些炫技的代码写法 一、编程语言介绍 本人主攻Java。下面我将介绍一下Java语言。 Java是一种广泛使用的高级编程语言&…

chatgpt赋能python:PythonShodan:极具威力的网络搜索引擎

Python Shodan&#xff1a;极具威力的网络搜索引擎 Python是一种流行的编程语言&#xff0c;被许多开发人员用来创建各种类型的应用程序和工具。其中一个强大的工具是Shodan&#xff0c;它是一个网络搜索引擎&#xff0c;可以帮助你找到任何与互联网连接的设备或系统。 什么是…

10款提效的在线设计工具推荐

在效率为王的时代&#xff0c;在线设计是设计的未来&#xff0c;为设计师提供了更节省时间、精力和成本的解决方案。 在线设计工具可以通过打开浏览器使用&#xff0c;大多数操作界面比传统设计工具更简单&#xff0c;入门门槛很低。 在这篇文章中&#xff0c;我们精心挑选了…

实验四:MapReduce初级编程实践

1.编程实现文件合并和去重操作 对于两个输入文件&#xff0c;即文件A和文件B,编写MapReduce程序&#xff0c;对两个文件进行合并&#xff0c; 并剔除其中重复的内容&#xff0c;得到一个新的输出文件C。下面是输入文件和输出文件的一个样 例供参考。 输入文件A的样例如下&#…

Tensorflow2基础代码实战系列之CNN文本分类实战

深度学习框架Tensorflow2系列 注&#xff1a;大家觉得博客好的话&#xff0c;别忘了点赞收藏呀&#xff0c;本人每周都会更新关于人工智能和大数据相关的内容&#xff0c;内容多为原创&#xff0c;Python Java Scala SQL 代码&#xff0c;CV NLP 推荐系统等&#xff0c;Spark …