SpringCloud Alibaba Sentinel实现熔断与限流
创始人
2024-01-17 17:36:19
0

目录

一、简介

1.官网 & 介绍

2.下载地址

3.作用

4.如何使用

⭐解决服务使用中的各种问题

5.Sentinel与Hystrix的区别

二、安装sentinel控制台

1.sentinel组件由2部分构成

2.安装步骤

①地址

②运行命令

③访问sentinel界面

三、初始化演示工程 

1.启动nacos8848

2.新建Module

①新建项目cloudalibaba-sentinel-service8401

②pom.xml

③application.yml

④主启动类

⑤业务类FlowLimitController

3.启动sentinel8080

4.启动微服务8401

5. 启动8401微服务后查看sentienl控制台

😵刷新后会发现没有什么变化

⭐结论

四、流控规则

1.基本介绍

2.流控模式

​编辑①直接(默认)------> 快速失败

②关联

③链路

3.流控效果

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

②预热

💧源码

③排队等待

五、降级规则

1.介绍

💧熔断策略

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

2.RT

​编辑①添加代码

②配置

③压测

④结论

3.异常比例

①定义

②测试 

③总结 

4.异常数

①定义

​编辑②测试

六、热点key限流

1.基本介绍

⚪官网

⚪热点参数规则

2.从HystrixCommand 到@SentinelResource

3.BlockException源码

4.编写代码

5.配置

6.测试 

7.参数例外项

①配置

②测试

③前提条件

8.其它

七、系统规则(系统自适应限流)

1.定义

2.各项配置参数说明

Load 自适应(仅对 Linux/Unix-like 机器生效)

CPU usage(1.5.0+ 版本)

平均 RT

并发线程数

入口 QPS

3.配置全局QPS

八、@SentinelResource

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

①分别启动nacos和sentinel

②新建Module

③配置流控规则

④测试

⑤其它问题

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

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

②业务类RateLimitController

③访问一次

④Sentinel控制台配置

⑤测试

3.上面兜底方案面临的问题

4.客户自定义限流处理逻辑

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

②自定义限流处理类

③RateLimitController

④启动微服务后先调用一次

⑤Sentinel控制台配置

⑥测试

⑦进一步说明

5.更多注解属性说明

①所有的代码都要用try-catch-finally方式进行处理,o(╥﹏╥)o

②Sentinel主要有三个核心Api

九、服务熔断功能

1.sentinel整合ribbon+openFeign+fallback

2.Ribbon系列

①启动nacos和sentinel

②提供者9003/9004

③消费者84

3.Feign系列

①修改84模块

②pom.xml

③application.xml

④业务类

⑤主启动

4.熔断框架比较

十、规则持久化

1.步骤

2.如何使用

3.步骤


一、简介

1.官网 & 介绍

GitHub - alibaba/Sentinel: A powerful flow control component enabling reliability, resilience and monitoring for microservices. (面向云原生微服务的高可用流控防护组件)

中文:https://github.com/alibaba/Sentinel/wiki/%E4%BB%8B%E7%BB%8D 

2.下载地址

Releases · alibaba/Sentinel (github.com)

链接:Spring Cloud Alibaba_免费高速下载|百度网盘-分享无限制 (baidu.com)
提取码:rt3w 

3.作用

4.如何使用

Spring Cloud Alibaba Reference Documentation (spring-cloud-alibaba-group.github.io)

⭐解决服务使用中的各种问题

5.Sentinel与Hystrix的区别

具体参考:技术选型:Sentinel vs Hystrix-阿里云开发者社区 (aliyun.com)

总结:

Hystrix常用的线程池隔离会造成线程上下切换的overhead比较大;Hystrix使用的信号量隔离对某个资源调用的并发数进行控制,效果不错,但是无法对慢调用进行自动降级;Sentinel通过并发线程数的流量控制提供信号量隔离的功能;

此外,Sentinel支持的熔断降级维度更多,可对多种指标进行流控、熔断,且提供了实时监控和控制面板,功能更为强大。

二、安装sentinel控制台

1.sentinel组件由2部分构成

2.安装步骤

①地址

链接:Spring Cloud Alibaba_免费高速下载|百度网盘-分享无限制 (baidu.com)
提取码:rt3w 

②运行命令

java -jar "E:\down\sentinel-dashboard-1.8.1 (1).jar"(这里使用的是绝对路径)

③访问sentinel界面

Sentinel Dashboard

(账号密码均为sentinel) 

三、初始化演示工程 

1.启动nacos8848

2.新建Module

①新建项目cloudalibaba-sentinel-service8401

②pom.xml


cloud2022com.atxupt.springcloud1.0-SNAPSHOT4.0.0cloudalibaba-sentinel-service840188com.alibaba.cloudspring-cloud-starter-alibaba-nacos-discoverycom.alibaba.cspsentinel-datasource-nacoscom.alibaba.cloudspring-cloud-starter-alibaba-sentinelorg.springframework.cloudspring-cloud-starter-openfeignorg.springframework.bootspring-boot-starter-weborg.springframework.bootspring-boot-starter-actuatororg.springframework.bootspring-boot-devtoolsruntimetruecn.hutoolhutool-all5.8.0org.projectlomboklomboktrueorg.springframework.bootspring-boot-starter-testtest

③application.yml

server:port: 8401spring:application:name: cloudalibaba-sentinel-servicecloud:nacos:discovery:#Nacos服务注册中心地址server-addr: localhost:8848sentinel:transport:#配置Sentinel dashboard地址dashboard: localhost:8080#默认8719端口,假如被占用会自动从8719开始依次+1扫描,直至找到未被占用的端口port: 8719management:endpoints:web:exposure:include: '*'

④主启动类

package com.atxupt.springcloud.alibaba;import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;@SpringBootApplication
@EnableDiscoveryClient
public class MainApp8401 {public static void main(String[] args) {SpringApplication.run(MainApp8401.class,args);}
}

⑤业务类FlowLimitController

package com.atxupt.springcloud.alibaba.controller;import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;@RestController
public class FlowLimitController {@GetMapping("/testA")public String testA(){return "------testA";}@GetMapping("/testB")public String testB(){return "------testB";}
}

3.启动sentinel8080

命令:java -jar "E:\down\sentinel-dashboard-1.8.1 (1).jar"

地址:Sentinel Dashboard

4.启动微服务8401

5. 启动8401微服务后查看sentienl控制台

😵刷新后会发现没有什么变化

原因:Sentinel采用的是懒加载(需要执行一次访问)

http://localhost:8401/testA

http://localhost:8401/testB

再次查看控制台

⭐结论

sentinel8080正在监控微服务8401         

四、流控规则

1.基本介绍

2.流控模式

①直接(默认)------> 快速失败

方式1:

方式2(效果相同):

这里采用方式1进行添加

单机阈值为1表示1秒钟内查询1次就是OK,若超过次数1,就直接-快速失败,报默认错误

测试:如果刷新速度过快,就会提示以下信息(被sentinel限流) 

思考!!!!! 

切换为线程数进行测试

疯狂点击刷新 

发现并不会有什么异常

在controller类中添加代码

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

再次疯狂点击刷新(注意!!!重启服务后需要重新配置sentinel中的内容)  

②关联

Ⅰ、配置A

  • 设置效果
  • 当关联资源/testB的qps阈值超过1时,就限流/testA的Rest访问地址
  • 当关联资源到阈值后限制配置好的资源名

Ⅱ、Apifox模拟并发密集访问testB

  • 添加相关信息

模拟并发密集访问testB 

Ⅲ、 运行测试

③链路

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

3.流控效果

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

②预热

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

官网:流量控制 · alibaba/Sentinel Wiki (github.com)

  • 默认coldFactor为3,即请求 QPS 从 threshold / 3 开始,经预热时长逐渐升至设定的 QPS 阈值。
  • 限流 冷启动:限流 冷启动 · alibaba/Sentinel Wiki (github.com) 
  • 当流量突然增大的时候,我们常常会希望系统从空闲状态到繁忙状态的切换的时间长一些。即如果系统在此之前长期处于空闲的状态,我们希望处理请求的数量是缓步的增多,经过预期的时间以后,到达系统处理请求个数的最大值。Warm Up(冷启动,预热)模式就是为了实现这个目的的。

  • 这个场景主要用于启动需要额外开销的场景,例如建立数据库连接等。

  • 它的实现是在 Guava 的算法的基础上实现的。然而,和 Guava 的场景不同,Guava 的场景主要用于调节请求的间隔,即 Leaky Bucket,而 Sentinel 则主要用于控制每秒的 QPS,即我们满足每秒通过的 QPS 即可,我们不需要关注每个请求的间隔,换言之,我们更像一个 Token Bucket。

  • 我们用桶里剩余的令牌来量化系统的使用率。假设系统每秒的处理能力为 b,系统每处理一个请求,就从桶中取走一个令牌;每秒这个令牌桶会自动掉落b个令牌。

  • 令牌桶越满,则说明系统的利用率越低;当令牌桶里的令牌高于某个阈值之后,我们称之为令牌桶"饱和"。

💧源码

com.alibaba.csp.sentinel.slots.block.flow.controller.WarmUpController

🥥WarmUp配置

  • 多次点击http://localhost:8401/testB :刚开始不行,后续慢慢OK
  • 应用场景(如:秒杀系统在开启的瞬间,会有很多流量上来,很有可能把系统打死,预热方式就是把为了保护系统,慢慢的把流量放进来,慢慢的把阈值增长到设置的阈值)

③排队等待

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

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

官网解释

测试 

为了打印出效果,修改FlowLimitController 代码,添加@Slf4j注解并打印信息       

(修改完成后需要重启项目,注意:可能需要在sentinel控制台重新配置!!!!!) 

@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";}
}

在Apifox中进行测试

五、降级规则

1.介绍

官网介绍:熔断降级 · alibaba/Sentinel Wiki · GitHub

  • 现代微服务架构都是分布式的,由非常多的服务组成。
  • 不同服务之间相互调用,组成复杂的调用链路。
  • 以上的问题在链路调用中会产生放大的效果。
  • 复杂链路上的某一环不稳定,就可能会层层级联,最终导致整个链路都不可用。
  • 因此我们需要对不稳定的弱依赖服务调用进行熔断降级
  • 暂时切断不稳定调用,避免局部不稳定因素导致整体的雪崩。
  • 熔断降级作为保护自身的手段
  • 通常在客户端(调用端)进行配置。 

💧熔断策略

Sentinel 提供以下几种熔断策略:

  • 慢调用比例 (SLOW_REQUEST_RATIO):选择以慢调用比例作为阈值,需要设置允许的慢调用 RT(即最大的响应时间),请求的响应时间大于该值则统计为慢调用。当单位统计时长(statIntervalMs)内请求数目大于设置的最小请求数目,并且慢调用的比例大于阈值,则接下来的熔断时长内请求会自动被熔断。经过熔断时长后熔断器会进入探测恢复状态(HALF-OPEN 状态),若接下来的一个请求响应时间小于设置的慢调用 RT 则结束熔断,若大于设置的慢调用 RT 则会再次被熔断。
  • 异常比例 (ERROR_RATIO):当单位统计时长(statIntervalMs)内请求数目大于设置的最小请求数目,并且异常的比例大于阈值,则接下来的熔断时长内请求会自动被熔断。经过熔断时长后熔断器会进入探测恢复状态(HALF-OPEN 状态),若接下来的一个请求成功完成(没有错误)则结束熔断,否则会再次被熔断。异常比率的阈值范围是 [0.0, 1.0],代表 0% - 100%。
  • 异常数 (ERROR_COUNT):当单位统计时长内的异常数目超过阈值之后会自动进行熔断。经过熔断时长后熔断器会进入探测恢复状态(HALF-OPEN 状态),若接下来的一个请求成功完成(没有错误)则结束熔断,否则会再次被熔断。

注意异常降级仅针对业务异常,对 Sentinel 限流降级本身的异常(BlockException)不生效。为了统计异常比例或异常数,需要通过 Tracer.trace(ex) 记录业务异常。

  • Sentinel 熔断降级会在调用链路中某个资源出现不稳定状态时(例如调用超时或异常比例升高),对这个资源的调用进行限制,让请求快速失败,避免影响到其它的资源而导致级联错误。
  • 当资源被降级后,在接下来的降级时间窗口之内,对该资源的调用都自动熔断(默认行为是抛出 DegradeException)。 

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

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

具体可以参考Hystrix

2.RT

①添加代码

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

②配置

慢调用比例 :选择以慢调比例作为阈值,要设置允许的慢调用 RT,请求的响应时间大于该值则统计为慢调用。单位统计时长内请求数目大于设置的最小请求数目,慢调用的比例大于阈值,则接下熔断时长内请求会自动熔断

③压测

暂停测试后,再次访问,恢复正常

④结论

按照上述配置,永远一秒钟进来10个线程(大于5个了)调用testD

我们希望200毫秒处理完本次任务,如果超过200毫秒还没处理完,在未来1秒钟的时间窗口内,断路器打开(保险丝跳闸)微服务不可用,保险丝跳闸断电了

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

3.异常比例

①定义

②测试 

编写代码

@GetMapping("/testD")
public String testD()
{log.info("testD 测试RT");int age = 10/0;return "------testD";
}

添加配置 

访问localhost:8401/testD 

按照上述配置单独访问一次 

③总结 

按照上述配置

单独访问一次,必然来一次报错一次(int age  = 10/0),调一次错一次;

  • 开启jmeter后,直接高并发发送请求,多次调用达到我们的配置条件了。
  • 断路器开启(保险丝跳闸),微服务不可用了,不再报错error而是服务降级了。

4.异常数

①定义

异常数是按照分钟统计的

时间窗口一定要大于等于60秒。

②测试

编写代码

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

直接访问会报错

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

六、热点key限流

1.基本介绍

  • 热点即经常访问的数据,很多时候我们希望统计或者限制某个热点数据中访问频次最高的TopN数据,并对其访问进行限流或者其它操作
  • 热点参数限流会统计传入参数中的热点参数,并根据配置的限流阈值与模式,对包含热点参数的资源调用进行限流。热点参数限流可以看做是一种特殊的流量控制,仅对包含热点参数的资源调用生效。
  • Sentinel 利用 LRU 策略统计最近最常访问的热点参数,结合令牌桶算法来进行参数级别的流控。
  • 热点参数限流支持集群模式。

⚪官网

热点参数限流 · alibaba/Sentinel Wiki (github.com)

 

⚪热点参数规则

2.从HystrixCommand 到@SentinelResource

3.BlockException源码

com.alibaba.csp.sentinel.slots.block.BlockException

/** Copyright 1999-2018 Alibaba Group Holding Ltd.** Licensed under the Apache License, Version 2.0 (the "License");* you may not use this file except in compliance with the License.* You may obtain a copy of the License at**      http://www.apache.org/licenses/LICENSE-2.0** Unless required by applicable law or agreed to in writing, software* distributed under the License is distributed on an "AS IS" BASIS,* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.* See the License for the specific language governing permissions and* limitations under the License.*/
package com.alibaba.csp.sentinel.slots.block;/*** Abstract exception indicating blocked by Sentinel due to flow control,* circuit breaking or system protection triggered.** @author youji.zj*/
public abstract class BlockException extends Exception {private static final int MAX_SEARCH_DEPTH = 10;public static final String BLOCK_EXCEPTION_FLAG = "SentinelBlockException";public static final String BLOCK_EXCEPTION_MSG_PREFIX = "SentinelBlockException: ";/*** 

this constant RuntimeException has no stack trace, just has a message* {@link #BLOCK_EXCEPTION_FLAG} that marks its name.*

*

* Use {@link #isBlockException(Throwable)} to check whether one Exception* Sentinel Blocked Exception.*

*/public static RuntimeException THROW_OUT_EXCEPTION = new RuntimeException(BLOCK_EXCEPTION_FLAG);public static StackTraceElement[] sentinelStackTrace = new StackTraceElement[] {new StackTraceElement(BlockException.class.getName(), "block", "BlockException", 0)};static {THROW_OUT_EXCEPTION.setStackTrace(sentinelStackTrace);}protected AbstractRule rule;private String ruleLimitApp;public BlockException(String ruleLimitApp) {super();this.ruleLimitApp = ruleLimitApp;}public BlockException(String ruleLimitApp, AbstractRule rule) {super();this.ruleLimitApp = ruleLimitApp;this.rule = rule;}public BlockException(String message, Throwable cause) {super(message, cause);}public BlockException(String ruleLimitApp, String message) {super(message);this.ruleLimitApp = ruleLimitApp;}public BlockException(String ruleLimitApp, String message, AbstractRule rule) {super(message);this.ruleLimitApp = ruleLimitApp;this.rule = rule;}@Overridepublic Throwable fillInStackTrace() {return this;}public String getRuleLimitApp() {return ruleLimitApp;}public void setRuleLimitApp(String ruleLimitApp) {this.ruleLimitApp = ruleLimitApp;}public RuntimeException toRuntimeException() {RuntimeException t = new RuntimeException(BLOCK_EXCEPTION_MSG_PREFIX + getClass().getSimpleName());t.setStackTrace(sentinelStackTrace);return t;}/*** Check whether the exception is sentinel blocked exception. One exception is sentinel blocked* exception only when:*
    *
  • the exception or its (sub-)cause is {@link BlockException}, or
  • *
  • the exception's message or any of its sub-cause's message is prefixed by {@link #BLOCK_EXCEPTION_FLAG}
  • *
** @param t the exception.* @return return true if the exception marks sentinel blocked exception.*/public static boolean isBlockException(Throwable t) {if (null == t) {return false;}int counter = 0;Throwable cause = t;while (cause != null && counter++ < MAX_SEARCH_DEPTH) {if (cause instanceof BlockException) {return true;}if (cause.getMessage() != null && cause.getMessage().startsWith(BLOCK_EXCEPTION_FLAG)) {return true;}cause = cause.getCause();}return false;}public AbstractRule getRule() {return rule;} }

4.编写代码

在FlowLimitController中添加代码

@GetMapping("/testHotKey")
@SentinelResource(value = "testHotKey",blockHandler = "del_testHotKey")
public String testHotkey(@RequestParam(value = "p1",required = false) String p1,@RequestParam(value = "p2",required = false) String p2){return "-------testHotKey";
}
//兜底的方法
public String del_testHotKey(String p1, String p2, BlockException exception){return "-------testHotKey,(((φ(◎ロ◎;)φ)))";
}

5.配置

  • 限流模式只支持QPS模式,固定写死了。(这才叫热点)
  • @SentinelResource注解的方法参数索引,0代表第一个参数,1代表第二个参数,以此类推
  • 单机阈值以及统计窗口时长表示在此窗口时间超过阈值就限流。
  • 上面的抓图就是第一个参数有值的话,1秒的QPS为1,超过就限流,限流后调用dealHandler_testHotKey支持方法

6.测试 

7.参数例外项

(类似vip特权)

①配置

配置完成后记得点击添加!!! 

②测试

localhost:8401/testHotKey?p1=5

③前提条件

热点参数的注意点,参数必须是基本类型或者String

8.其它

七、系统规则(系统自适应限流)

1.定义

系统自适应限流 · alibaba/Sentinel Wiki (github.com)

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

2.各项配置参数说明

  • 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 达到阈值即触发系统保护。

3.配置全局QPS

八、@SentinelResource

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

①分别启动nacos和sentinel

②新建Module

  • 修改模块cloudalibaba-sentinel-service8401中的配置
  • pom.xml

添加配置

  • application.yml
 
server:port: 8401spring:application:name: cloudalibaba-sentinel-servicecloud:nacos:discovery:server-addr: localhost:8848 #Nacos服务注册中心地址sentinel:transport:dashboard: localhost:8080 #配置Sentinel dashboard地址port: 8719management:endpoints:web:exposure:include: '*'
  • 业务类RateLimitController
package com.atxupt.springcloud.alibaba.controller;import com.alibaba.csp.sentinel.annotation.SentinelResource;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.atxupt.springcloud.entities.CommonResult;
import com.atxupt.springcloud.entities.Payment;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;@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 服务不可用");}
}
  • 主启动类(没有变化)

③配置流控规则

项目重新启动后访问:localhost:8401/byResource

  • 配置步骤

当刷新速率过快 

  • 图形配置和代码关系

  • 表示1秒钟内查询次数大于1,就跑到我们自定义的处流,限流

④测试

  • 1秒钟点击1下,OK

  • 超过上述,疯狂点击,返回了自己定义的限流处理信息,限流发生其它的问题

⑤其它问题

  • 此时关闭问服务8401看看

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

临时/持久?

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

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

②业务类RateLimitController

package com.atxupt.springcloud.alibaba.controller;import com.alibaba.csp.sentinel.annotation.SentinelResource;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.atxupt.springcloud.entities.CommonResult;
import com.atxupt.springcloud.entities.Payment;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;@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 服务不可用");}@GetMapping("/rateLimit/byUrl")@SentinelResource(value = "byUrl")public CommonResult byUrl(){return new CommonResult(200,"按url限流测试OK",new Payment(2020L,"serial002"));}
}

③访问一次

http://localhost:8401/rateLimit/byUrl

④Sentinel控制台配置

⑤测试

疯狂点击localhost:8401/rateLimit/byUrl

结果(会返回Sentinel自带的限流处理结果)

3.上面兜底方案面临的问题

4.客户自定义限流处理逻辑

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

②自定义限流处理类

import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.atxupt.springcloud.entities.CommonResult;public class CustomerBlockHandler {public static CommonResult handleException(BlockException exception){return new CommonResult(2020,"自定义的限流处理信息......CustomerBlockHandler1");}public static CommonResult handleException2(BlockException exception){return new CommonResult(2020,"自定义的限流处理信息......CustomerBlockHandler2");}
}

③RateLimitController

package com.atxupt.springcloud.alibaba.controller;import com.alibaba.csp.sentinel.annotation.SentinelResource;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.atxupt.springcloud.alibaba.myhandler.CustomerBlockHandler;
import com.atxupt.springcloud.entities.CommonResult;
import com.atxupt.springcloud.entities.Payment;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;@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 服务不可用");}@GetMapping("/rateLimit/byUrl")@SentinelResource(value = "byUrl")public CommonResult byUrl(){return new CommonResult(200,"按url限流测试OK",new Payment(2020L,"serial002"));}/*** 自定义通用的限流处理逻辑,blockHandlerClass = CustomerBlockHandler.classblockHandler = handleException2上述配置:找CustomerBlockHandler类里的handleException2方法进行兜底处理*//*** 自定义通用的限流处理逻辑*/@GetMapping("/rateLimit/customerBlockHandler")@SentinelResource(value = "customerBlockHandler",blockHandlerClass = CustomerBlockHandler.class, blockHandler = "handleException2")public CommonResult customerBlockHandler() {return new CommonResult(200,"按客户自定义限流处理逻辑");}
}

④启动微服务后先调用一次

⑤Sentinel控制台配置

⑥测试

快速刷新

⑦进一步说明

5.更多注解属性说明

①所有的代码都要用try-catch-finally方式进行处理,o(╥﹏╥)o

②Sentinel主要有三个核心Api

九、服务熔断功能

1.sentinel整合ribbon+openFeign+fallback

2.Ribbon系列

①启动nacos和sentinel

②提供者9003/9004

⚪pom.xml 


cloud2022com.atxupt.springcloud1.0-SNAPSHOT4.0.0cloudalibaba-provider-payment900488com.alibaba.cloudspring-cloud-starter-alibaba-nacos-discoverycom.atxupt.springcloudcloud-api-commons1.0-SNAPSHOTorg.springframework.bootspring-boot-starter-weborg.springframework.bootspring-boot-starter-actuatororg.springframework.bootspring-boot-devtoolsruntimetrueorg.projectlomboklomboktrueorg.springframework.bootspring-boot-starter-testtest

⚪application.yml

  • 9003的application.yml
server:port: 9003spring:application:name: nacos-payment-providercloud:nacos:discovery:server-addr: localhost:8848 #配置Nacos地址management:endpoints:web:exposure:include: '*'
  • 9004的application.yml
server:port: 9004spring:application:name: nacos-payment-providercloud:nacos:discovery:server-addr: localhost:8848 #配置Nacos地址management:endpoints:web:exposure:include: '*'

⚪主启动类

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

⚪业务类

package com.atxupt.springcloud.alibaba.controller;import com.atxupt.springcloud.entities.CommonResult;
import com.atxupt.springcloud.entities.Payment;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;import java.util.HashMap;@RestController
public class PaymentController
{@Value("${server.port}")private String serverPort;public static HashMap 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 paymentSQL(@PathVariable("id") Long id){Payment payment = hashMap.get(id);CommonResult result = new CommonResult(200,"from mysql,serverPort:  "+serverPort,payment);return result;}
}

⚪测试地址 

http://localhost:9003/paymentSQL/1

http://localhost:9004/paymentSQL/1

③消费者84

🐟 新建项目cloudalibaba-consumer-nacos-order84

🐟 pom.xml


cloud2022com.atxupt.springcloud1.0-SNAPSHOT4.0.0cloudalibaba-consumer-nacos-order8488com.alibaba.cloudspring-cloud-starter-alibaba-nacos-discoverycom.alibaba.cloudspring-cloud-starter-alibaba-sentinelcom.atxupt.springcloudcloud-api-commons1.0-SNAPSHOTorg.springframework.bootspring-boot-starter-weborg.springframework.bootspring-boot-starter-actuatororg.springframework.bootspring-boot-devtoolsruntimetrueorg.projectlomboklomboktrueorg.springframework.bootspring-boot-starter-testtest

🐟 application.yml

server:port: 84spring:application:name: nacos-order-consumercloud:nacos:discovery:server-addr: localhost:8848sentinel:transport:#配置Sentinel dashboard地址dashboard: localhost:8080#默认8719端口,假如被占用会自动从8719开始依次+1扫描,直至找到未被占用的端口port: 8719#消费者将要去访问的微服务名称(注册成功进nacos的微服务提供者)
service-url:nacos-user-service: http://nacos-payment-provider

🐟 主启动类

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

🐟 业务类

  • 🐟 🐟 ApplicationContextConfig
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;@Configuration
public class ApplicationContextConfig
{@Bean@LoadBalancedpublic RestTemplate getRestTemplate(){return new RestTemplate();}
}
  • 🐟 🐟 CircleBreakerController 

import com.alibaba.csp.sentinel.annotation.SentinelResource;
import com.atxupt.springcloud.entities.CommonResult;
import com.atxupt.springcloud.entities.Payment;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;import javax.annotation.Resource;
@RestController
@Slf4j
public class CircleBreakerController
{public static final String SERVICE_URL = "http://nacos-payment-provider";@Resourceprivate RestTemplate restTemplate;@RequestMapping("/consumer/fallback/{id}")@SentinelResource(value = "fallback")public CommonResult fallback(@PathVariable Long id){CommonResult 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;}
}

分别启动84、9003、9004 

测试地址:http://localhost:84/consumer/fallback/1 

此时的sentinel控制台

  • 没有任何配置时: 给客户返回error页面,看起来不友好

  • 只配置fallback 

@RestController
@Slf4j
public class CircleBreakerController
{public static final String SERVICE_URL = "http://nacos-payment-provider";@Resourceprivate RestTemplate restTemplate;@RequestMapping("/consumer/fallback/{id}")//1.@SentinelResource(value = "fallback")  什么都没有配置//fallback只负责业务异常@SentinelResource(value = "fallback",fallback = "handlerFallback")public CommonResult fallback(@PathVariable Long id){CommonResult 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;}//fallbackpublic CommonResult handlerFallback(@PathVariable  Long id,Throwable e) {Payment payment = new Payment(id,"null");return new CommonResult<>(444,"兜底异常handlerFallback,exception内容  "+e.getMessage(),payment);}

重启项目

访问localhost:84/consumer/fallback/4

  • 只配置blockHandler

@RestController
@Slf4j
public class CircleBreakerController
{public static final String SERVICE_URL = "http://nacos-payment-provider";@Resourceprivate RestTemplate restTemplate;@RequestMapping("/consumer/fallback/{id}")//1.@SentinelResource(value = "fallback")  什么都没有配置//2.只配置fallback,fallback只负责业务异常// @SentinelResource(value = "fallback",fallback = "handlerFallback")//3.只配置blockHandler,blockHandler只负责sentinel控制台配置违规@SentinelResource(value = "fallback",blockHandler = "blockHandler")public CommonResult fallback(@PathVariable Long id){CommonResult 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);
//    }//blockHandlerpublic CommonResult blockHandler(@PathVariable  Long id, BlockException blockException) {Payment payment = new Payment(id,"null");return new CommonResult<>(445,"blockHandler-sentinel限流,无此流水: blockException  "+blockException.getMessage(),payment);}
}

重新启动84项目,由于配置了blockHandler,因此需要在sentinel控制台进行配置

第一次发起请求 

疯狂点击几次后:

id换为5会有同样的效果

  • fallback和blockHandler都配置
@RestController
@Slf4j
public class CircleBreakerController
{public static final String SERVICE_URL = "http://nacos-payment-provider";@Resourceprivate RestTemplate restTemplate;@RequestMapping("/consumer/fallback/{id}")//1.@SentinelResource(value = "fallback")  什么都没有配置//2.只配置fallback,fallback只负责业务异常//@SentinelResource(value = "fallback",fallback = "handlerFallback")//3.只配置blockHandler,blockHandler只负责sentinel控制台配置违规//@SentinelResource(value = "fallback",blockHandler = "blockHandler")//4.两个都配置@SentinelResource(value = "fallback",fallback = "handlerFallback",blockHandler = "blockHandler")public CommonResult fallback(@PathVariable Long id){CommonResult 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;}
//    //fallbackpublic CommonResult handlerFallback(@PathVariable  Long id,Throwable e) {Payment payment = new Payment(id,"null");return new CommonResult<>(444,"兜底异常handlerFallback,exception内容  "+e.getMessage(),payment);}//blockHandlerpublic CommonResult blockHandler(@PathVariable  Long id, BlockException blockException) {Payment payment = new Payment(id,"null");return new CommonResult<>(445,"blockHandler-sentinel限流,无此流水: blockException  "+blockException.getMessage(),payment);}
}

重启项目后添加配置 

访问速度过快

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

  • 忽略的属性

@RestController
@Slf4j
public class CircleBreakerController
{public static final String SERVICE_URL = "http://nacos-payment-provider";@Resourceprivate RestTemplate restTemplate;@RequestMapping("/consumer/fallback/{id}")//1.@SentinelResource(value = "fallback")  什么都没有配置//2.只配置fallback,fallback只负责业务异常//@SentinelResource(value = "fallback",fallback = "handlerFallback")//3.只配置blockHandler,blockHandler只负责sentinel控制台配置违规//@SentinelResource(value = "fallback",blockHandler = "blockHandler")//4.两个都配置
//    @SentinelResource(value = "fallback",fallback = "handlerFallback",blockHandler = "blockHandler")//5.本例sentinel无配置@SentinelResource(value = "fallback",fallback = "handlerFallback",blockHandler = "blockHandler",exceptionsToIgnore = {IllegalArgumentException.class})public CommonResult fallback(@PathVariable Long id){CommonResult 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;}//fallbackpublic CommonResult handlerFallback(@PathVariable  Long id,Throwable e) {Payment payment = new Payment(id,"null");return new CommonResult<>(444,"兜底异常handlerFallback,exception内容  "+e.getMessage(),payment);}//blockHandlerpublic CommonResult blockHandler(@PathVariable  Long id, BlockException blockException) {Payment payment = new Payment(id,"null");return new CommonResult<>(445,"blockHandler-sentinel限流,无此流水: blockException  "+blockException.getMessage(),payment);}
}

 

3.Feign系列

①修改84模块

  • 84消费者调用提供者9003
  • Feign组件一般是消费侧

②pom.xml

引入依赖


org.springframework.cloudspring-cloud-starter-openfeign

③application.xml

添加配置

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

④业务类

  • Ⅰ、带@FeignClient注解的业务接口
import com.atxupt.springcloud.entities.CommonResult;
import com.atxupt.springcloud.entities.Payment;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;/*** 使用 fallback 方式是无法获取异常信息的,* 如果想要获取异常信息,可以使用 fallbackFactory参数*/
@FeignClient(value = "nacos-payment-provider",fallback = PaymentFallbackService.class)//调用中关闭9003服务提供者
public interface PaymentService
{@GetMapping(value = "/paymentSQL/{id}")public CommonResult paymentSQL(@PathVariable("id") Long id);
}
  • Ⅱ、fallback = PaymentFallbackService.class
import com.atxupt.springcloud.entities.CommonResult;
import com.atxupt.springcloud.entities.Payment;
import org.springframework.stereotype.Component;@Component
public class PaymentFallbackService implements PaymentService
{@Overridepublic CommonResult paymentSQL(Long id){return new CommonResult<>(444,"服务降级返回,没有该流水信息",new Payment(id, "errorSerial......"));}
}
  • Ⅲ、Controller
@RestController
@Slf4j
public class CircleBreakerController
{public static final String SERVICE_URL = "http://nacos-payment-provider";@Resourceprivate RestTemplate restTemplate;@RequestMapping("/consumer/fallback/{id}")//1.@SentinelResource(value = "fallback")  什么都没有配置//2.只配置fallback,fallback只负责业务异常//@SentinelResource(value = "fallback",fallback = "handlerFallback")//3.只配置blockHandler,blockHandler只负责sentinel控制台配置违规//@SentinelResource(value = "fallback",blockHandler = "blockHandler")//4.两个都配置
//    @SentinelResource(value = "fallback",fallback = "handlerFallback",blockHandler = "blockHandler")//5.本例sentinel无配置@SentinelResource(value = "fallback",fallback = "handlerFallback",blockHandler = "blockHandler",exceptionsToIgnore = {IllegalArgumentException.class})public CommonResult fallback(@PathVariable Long id){CommonResult 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;}//fallbackpublic CommonResult handlerFallback(@PathVariable  Long id,Throwable e) {Payment payment = new Payment(id,"null");return new CommonResult<>(444,"兜底异常handlerFallback,exception内容  "+e.getMessage(),payment);}//blockHandlerpublic CommonResult blockHandler(@PathVariable  Long id, BlockException blockException) {Payment payment = new Payment(id,"null");return new CommonResult<>(445,"blockHandler-sentinel限流,无此流水: blockException  "+blockException.getMessage(),payment);}//OpenFeign@Resourceprivate PaymentService paymentService;@GetMapping(value = "/consumer/openfeign/{id}")public CommonResult paymentSQL(@PathVariable("id") Long id) {if(id == 4){throw new RuntimeException("没有该id");}return paymentService.paymentSQL(id);}
}

⑤主启动

添加@EnableFeignClients启动Feign的功能

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

http://localhost:84/consumer/paymentSQL/1

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

4.熔断框架比较

十、规则持久化

1.步骤

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

2.如何使用

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

3.步骤

相关内容

热门资讯

数学赛课的通讯稿 数学赛课的三篇通讯稿  数学赛课通讯稿篇一:公开课通讯稿  十月的天空格外的清爽,暖暖的阳光洒在身上...
一个数除以分数说课稿 一个数除以分数说课稿  作为一无名无私奉献的教育工作者,就有可能用到说课稿,通过说课稿可以很好地改正...
少年中国说朗诵稿全版 我们肩负沉甸甸的嘱托,我们憧憬美好的未来。透过历史的眼眸,我们站在岁月的肩膀上远眺。下面是unjs小...
《蹲踞式起跑》说课稿   《蹲踞式起跑》这节体育课怎么上好呢?下面是应届毕业生小编为大家收集的关于《蹲踞式起跑》说课稿,希...
结婚证婚人讲话稿 结婚证婚人讲话稿范文(精选8篇)  在我们平凡的日常里,越来越多地方需要用到讲话稿,讲话稿的作用是辅...
电台新闻节目广播稿 电台新闻节目广播稿(通用12篇)  有在广播站锻炼的学生,我们广播前都会提前准备好广播稿,好的广播稿...
《牛顿第一定律》说课稿 《牛顿第一定律》说课稿  今天我说课的内容是一个科学探究的实验:牛顿第一定律。  一、教材分析:  ...
《折扣》语文说课材料 《折扣》语文说课材料  一、说教材  《折扣》是六年级上册数学课本中第五单元中的一节课。它是在学生学...
小学一年级班主任经验交流发言... 小学一年级班主任经验交流发言稿(精选10篇)  现如今,发言稿使用的情况越来越多,发言稿可以帮助发言...
致女子跳远加油稿 致女子跳远加油稿(通用11篇)  在充满活力,日益开放的今天,用到加油稿的地方越来越多,加油稿可以激...
开学第一课国旗下讲话稿 开学第一课国旗下讲话稿  开学第一课国旗下讲话稿(精选30篇)  新的学期开始了,开学典礼也要开始了...
表态发言稿 表态发言稿800字(精选21篇)  在当下社会,发言稿在我们的视野里出现的频率越来越高,好的发言稿可...
《乡下人家》说课稿 《乡下人家》说课稿范文(精选10篇)  引导语:作为一位不辞辛劳的人民教师,常常要根据教学需要编写说...
招商引资讲话稿 招商引资讲话稿(精选16篇)  在当下社会,接触并使用讲话稿的人越来越多,讲话稿是讲话者为了在会议或...
学生会宣传部竞选的演讲稿 学生会宣传部竞选的演讲稿  各位评委老师,各位同学,大家好:  在这里我想先说明一下:过去的已成为过...
环保的发言稿 关于环保的发言稿6篇  现如今,越来越多人会去使用发言稿,发言稿特别注重结构清楚,层次简明。那么问题...
改进作风发言稿 改进作风发言稿(精选5篇)  在社会发展不断提速的今天,在很多情况下我们需要用到发言稿,发言稿可以起...
大学生晚会新闻稿 大学生晚会新闻稿(通用6篇)  在社会一步步向前发展的今天,新闻稿使用的次数愈发增长,新闻作为一种以...
春季开学第一天国旗下讲话稿 春季开学第一天国旗下讲话稿(精选3篇)  在当下社会,很多情况下我们需要用到讲话稿,讲话稿包括人们在...
《鱼游到了纸上》说课稿 《鱼游到了纸上》说课稿范文(精选6篇)  作为一无名无私奉献的教育工作者,通常会被要求编写说课稿,编...