Sentinel熔断和断流
1. Sentinel介绍
Sentinel 是面向分布式、多语言异构化服务架构的流量治理组件,主要以流量为切入点,从流量路由、流量控制、流量整形、熔断降级、系统自适应过载保护、热点流量防护等多个维度来帮助开发者保障微服务的稳定性。
等同于Spring Cloud Circuit Breaker。
2. Sentinel的特性
- 丰富的应用场景:Sentinel承接了阿里巴巴近10年的双十一大促流量的核心场景,例如秒杀(即突发流量控制在系统容量可以承受的范围)、消息削峰填谷、集群流量控制、实时熔断下游不可用应用等。
- 完备的实时监控:Sentinel同时提供实时的监控功能。您可以在控制台中看到接入应用的单台机器秒级数据,甚至 500 台以下规模的集群的汇总运行情况。
- 广泛的开源生态:Sentinel提供开箱即用的与其它开源框架/库的整合模块,例如与Spring Cloud、Apache Dubbo、gRPC、Quarkus的整合。您只需要引入相应的依赖并进行简单的配置即可快速地接入 Sentinel。同时Sentinel提供Java/Go/C++等多语言的原生实现。
- 完善的SPI扩展机制:Sentinel 提供简单易用、完善的SPI扩展接口。您可以通过实现扩展接口来快速地定制逻辑。例如定制规则管理、适配动态数据源等。
3. 安装Sentinel
sentinel组件由2部分构成:后台端口默认8719,前台端口默认8080。
3.1 下载Sentinel
访问https://github.com/alibaba/Sentinel/releases/tag/ ,下载sentinel-dashboard-1.8.8.jar
3.2 启动Sentinel
使用java命令启动
java -jar sentinel-dashboard-1.8.8.jar
访问http://localhost:8080 ,用户名/密码都是sentinel/sentinel
4. 创建sentinel-service模块
- 创建新模块cloudalibaba-sentinel-service8401
- pom中添加如下依赖:
dependencies>
<!--SpringCloud alibaba sentinel -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
<!--nacos-discovery-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<!-- 引入自己定义的api通用包 -->
<dependency>
<groupId>com.rocket.cloud</groupId>
<artifactId>cloud-api-commons</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<!--SpringBoot通用依赖模块-->
<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>
<!--hutool-->
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
</dependency>
<!--lombok-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.28</version>
<scope>provided</scope>
</dependency>
<!--test-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
- 配置application.yml
server:
port: 8401
spring:
application:
name: cloudalibaba-sentinel-service
cloud:
nacos:
discovery:
server-addr: localhost:8848 #Nacos服务注册中心地址
sentinel:
transport:
dashboard: localhost:8080 #配置Sentinel dashboard控制台服务地址
port: 8719 #默认8719端口,假如被占用会自动从8719开始依次+1扫描,直至找到未被占用的端口
- 创建Main8401\FlowLimitController类:
@EnableDiscoveryClient
@SpringBootApplication
public class Main8401 {
public static void main(String[] args) {
SpringApplication.run(Main8401.class,args);
}
}
@RestController
public class FlowLimitController {
@GetMapping("/testA")
public String testA() {
return "------testA";
}
@GetMapping("/testB")
public String testB() {
return "------testB";
}
}
- 启动程序发现sentinel控制台空空如也,原来Sentinel采用的懒加载,需要访问一下接口:
多访问几次接口,页面出现了数据:
5. 使用流控规则
Sentinel能够对流量进行控制,主要是监控应用的QPS流量或者并发线程数等指标,如果达到指定的阈值时,就会被流量进行控制,以避免服务被瞬时的高并发流量击垮,保证服务的高可靠性。 配置说明:
1.资源名: 资源的唯一名称,默认就是请求的接口路径,可以自行修改,但是要保证唯一。
2.针对来源:具体针对某个微服务进行限流,默认值为default,表示不区分来源,全部限流。
3.阈值类型: QPS表示通过QPS进行限流,并发线程数表示通过并发线程数限流。
4.单机阈值: 与阈值类型组合使用。如果阈值类型选择的是QPS,表示当调用接口的QPS达到阈值时,进行限流操作。如果阈值类型选择的是并发线程数,则表示当调用接口的并发线程数达到阈值时,进行限流操作。
5.是否集群: 选中则表示集群环境,不选中则表示非集群环境。
5.1 流控模式
5.1.1 直接
默认的流控模式,当接口达到限流条件时,直接开启限流功能。比如配置如图: 表示每秒处理请求限制2次,超过就报错:
5.1.2 关联
当关联的资源达到阈值时,就限流自己 当关联资源/testB的qps阀值超过1时,就限流/testA的Rest访问地址,当关联资源到阈值后限制配置好的资源名,B惹事,A挂了。
使用Jmeter模拟并发密集访问testB,添加线程组: 只要每秒超过1次请求就可以:
再在Thread Group基础上添加Http请求配置
点击执行:
刷新浏览器发现页面结果一直持续5秒出现:
5.1.3 链路
来自不同链路的请求对同一个目标访问时,实施针对性的不同限流措施。比如C请求来访问就限流,D请求来访问就是OK。
修改application.yml:
server:
port: 8401
spring:
application:
name: cloudalibaba-sentinel-service #8401微服务提供者后续将会被纳入阿里巴巴sentinel监管
cloud:
nacos:
discovery:
server-addr: localhost:8848 #Nacos服务注册中心地址
sentinel:
transport:
dashboard: localhost:8080 #配置Sentinel dashboard控制台服务地址
port: 8719 #默认8719端口,假如被占用会自动从8719开始依次+1扫描,直至找到未被占用的端口
web-context-unify: false # controller层的方法对service层调用不认为是同一个根链路
添加FlowLimitService类和在FlowLimitController中添加调用逻辑:
@Service
public class FlowLimitService {
@SentinelResource(value = "common")
public void common() {
System.out.println("------FlowLimitService come in");
}
}
@Resource
private FlowLimitService flowLimitService;
@GetMapping("/testC")
public String testC() {
flowLimitService.common();
return "------testC";
}
@GetMapping("/testD")
public String testD() {
flowLimitService.common();
return "------testD";
}
添加C和D两个请求都访问flowLimitService.common()方法,对C限流,对D不管: /testC超过一秒钟一次后,就发生限流:
5.2 流控效果
5.2.1 直接
快速失败(默认的流控处理),失败抛出异常。
5.2.2 预热Warmup
当流量突然增大的时候,我们常常会希望系统从空闲状态到繁忙状态的切换的时间长一些。这个场景主要用于启动需要额外开销的场景,例如建立数据库连接等。
默认coldFactor为3,即请求QPS从(threshold/3) 开始,经多少预热时长才逐渐升至设定的QPS阈值。 单机阈值为10,预热时长设置5秒。系统初始化的阈值为10/3约等于3,即单机阈值刚开始为3(我们人工设定单机阈值是10,sentinel计算后QPS判定为3开始);然后过了5秒后阀值才慢慢升高恢复到设置的单机阈值10,也就是说5秒钟内QPS为3,过了保护期5秒后QPS为10。
多次点击http://localhost:8401/testB刚开始不行,后续慢慢OK
5.2.3 排队等待
这种方式主要用于处理间隔性突发的流量,但是匀速排队模式暂时不支持QPS>1000的场景。 在FlowLimitController中添加/testE接口:
@GetMapping("/testE")
public String testE() {
System.out.println(System.currentTimeMillis()+" testE,排队等待");
return "------testE";
}
按照单机阈值,一秒钟通过一个请求,10秒后的请求作为超时处理并放弃。
6. 熔断规则
Sentinel 熔断降级会在调用链路中某个资源出现不稳定状态时(例如调用超时或异常比例升高),对这个资源的调用进行限制,让请求快速失败,避免影响到其它的资源而导致级联错误。当资源被降级后,在接下来的降级时间窗口之内,对该资源的调用都自动熔断(默认行为是抛出 DegradeException)。 Sentinel主要提供了三个熔断策略:慢调用比例,异常比例,异常数。
6.1 慢调用比例
慢调用比例(SLOW_REQUEST_RATIO):选择以慢调用比例作为阈值,需要设置允许的慢调用RT(即最大的响应时间),请求的响应时间大于该值则统计为慢调用。当单位统计时长(statIntervalMs)内请求数目大于设置的最小请求数目,并且慢调用的比例大于阈值,则接下来的熔断时长内请求会自动被熔断。经过熔断时长后熔断器会进入探测恢复状态(HALF-OPEN状态),若接下来的一个请求响应时间小于设置的慢调用RT则结束熔断,若大于设置的慢调用RT则会再次被熔断。如下图: 其中最大RT就是判断慢查询的,配置的是超过200ms就是慢查询。整个熔断判断逻辑是判断1s内如果超过5个请求,有超过0.1比例的连接是慢查询就开始熔断,熔断时长持续5s后,开始试探放出1个请求,如果不是慢查询则结束熔断。
进入熔断状态判断依据:在统计时长内,实际请求数目>设定的最小请求数且实际慢调用比例>比例阈值,进入熔断状态。
- 熔断状态(保险丝跳闸断电,不可访问):在接下来的熔断时长内请求会自动被熔断
- 探测恢复状态(探路先锋):熔断时长结束后进入探测恢复状态
- 结束熔断(保险丝闭合恢复,可以访问):在探测恢复状态,如果接下来的一个请求响应时间小于设置的慢调用 RT,则结束熔断,否则继续熔断。
6.2 异常比例
异常比例(ERROR_RATIO):当单位统计时长(statIntervalMs)内请求数目大于设置的最小请求数目,并且异常的比例大于阈值,则接下来的熔断时长内请求会自动被熔断。经过熔断时长后熔断器会进入探测恢复状态(HALF-OPEN状态),若接下来的一个请求成功完成(没有错误)则结束熔断,否则会再次被熔断。异常比率的阈值范围是 [0.0, 1.0],代表0%-100%。
上面的接口中若加上int age = 10/0
,每访问一次,必然来一次报错一次达到100%,调一次错一次报错error; 开启jmeter后,直接高并发发送请求,调用达到我们的配置条件了。
断路器开启(保险丝跳闸),微服务不可用了,不再报错error而是服务熔断+服务降级,出提示
Blocked by Sentinel (flow limiting)
。
6.3 异常数
异常数(ERROR_COUNT):当单位统计时长内的异常数目超过阈值之后会自动进行熔断。经过熔断时长后熔断器会进入探测恢复状态(HALF-OPEN状态),若接下来的一个请求成功完成(没有错误)则结束熔断,否则会再次被熔断。 上述配置表示,在1秒钟内最少请求2次,当异常数大于1时,会触发熔断操作断路器开启(保险丝跳闸),微服务不可用了,熔断的时长为5秒,不再报错error而是服务降级了出提示
Blocked by Sentinel (flow limiting)
7. SentinelResource注解
@SentinelResource是一个流量防卫防护组件注解,用于指定防护资源,对配置的资源进行流量控制、熔断降级等功能。查看源码:
public @interface SentinelResource {
//资源名称
String value() default "";
//entry类型,标记流量的方向,取值IN/OUT,默认是OUT
EntryType entryType() default EntryType.OUT;
//资源分类
int resourceType() default 0;
//处理BlockException的函数名称,函数要求:
//1. 必须是 public
//2.返回类型 参数与原方法一致
//3. 默认需和原方法在同一个类中。若希望使用其他类的函数,可配置blockHandlerClass ,并指定blockHandlerClass里面的方法。
String blockHandler() default "";
//存放blockHandler的类,对应的处理函数必须static修饰。
Class<?>[] blockHandlerClass() default {};
//用于在抛出异常的时候提供fallback处理逻辑。fallback函数可以针对所
//有类型的异常(除了 exceptionsToIgnore 里面排除掉的异常类型)进行处理。函数要求:
//1. 返回类型与原方法一致
//2. 参数类型需要和原方法相匹配
//3. 默认需和原方法在同一个类中。若希望使用其他类的函数,可配置fallbackClass ,并指定fallbackClass里面的方法。
String fallback() default "";
//存放fallback的类。对应的处理函数必须static修饰。
String defaultFallback() default "";
//用于通用的 fallback 逻辑。默认fallback函数可以针对所有类型的异常进
//行处理。若同时配置了 fallback 和 defaultFallback,以fallback为准。函数要求:
//1. 返回类型与原方法一致
//2. 方法参数列表为空,或者有一个 Throwable 类型的参数。
//3. 默认需要和原方法在同一个类中。若希望使用其他类的函数,可配置fallbackClass ,并指定 fallbackClass 里面的方法。
Class<?>[] fallbackClass() default {};
//需要trace的异常
Class<? extends Throwable>[] exceptionsToTrace() default {Throwable.class};
//指定排除忽略掉哪些异常。排除的异常不会计入异常统计,也不会进入fallback逻辑,而是原样抛出。
Class<? extends Throwable>[] exceptionsToIgnore() default {};
}
7.1 按rest地址限流+默认限流返回
编写RateLimitController类:
@RestController
@Slf4j
public class RateLimitController {
@GetMapping("/rateLimit/byUrl")
public String byUrl() {
return "按rest地址限流测试OK";
}
}
配置sentinel页面,可以点击簇点链路菜单,选择/rateLimit/byUrl资源,点击添加流控: 重启cloudalibaba-sentinel-service8401后,简单配置QPS超过1就熔断:
熔断默认为
Blocked by Sentinel (flow limiting)
:
7.2 按资源名称限流+自定义限流返回
在RateLimitController类中添加自定义限流返回代码:
@GetMapping("/rateLimit/byResource")
@SentinelResource(value = "byResourceSentinelResource",blockHandler = "handleException")
public String byResource() {
return "按资源名称SentinelResource限流测试OK";
}
public String handleException(BlockException exception) {
return "服务不可用@SentinelResource启动"+"\t"+"o(╥﹏╥)o";
}
重启cloudalibaba-sentinel-service8401后,配置流控规则: 疯狂访问/rateLimit/byResource资源:
7.3 按资源名称限流+自定义限流返回+服务降级处理
在RateLimitController类中添加自定义限流+服务降级处理返回代码:
@GetMapping("/rateLimit/doAction/{p1}")
@SentinelResource(value = "doActionSentinelResource",
blockHandler = "doActionBlockHandler", fallback = "doActionFallback")
public String doAction(@PathVariable("p1") Integer p1) {
if (p1 == 0){
throw new RuntimeException("p1等于零直接异常");
}
return "doAction";
}
public String doActionBlockHandler(@PathVariable("p1") Integer p1,BlockException e){
log.error("sentinel配置自定义限流了:{}", e);
return "sentinel配置自定义限流了";
}
public String doActionFallback(@PathVariable("p1") Integer p1,Throwable e){
log.error("程序逻辑异常了:{}", e);
return "程序逻辑异常了"+"\t"+e.getMessage();
}
重启cloudalibaba-sentinel-service8401后,配置流控规则: 疯狂访问/rateLimit/doAction/1会被发现被限流,并返回自定义的限流信息:
访问/rateLimit/doAction/0会被返回自定义服务降级信息:
提示
blockHandler: 主要针对sentinel配置后出现的违规情况处理。
fallback: 程序异常了JVM抛出的异常服务降级。
8. 热点规则
热点即经常访问的数据,很多时候我们希望统计或者限制某个热点数据中访问频次最高的TopN数据,并对其访问进行限流或者其它操作。 在RateLimitController中添加下面接口代码:
@GetMapping("/testHotKey")
@SentinelResource(value = "testHotKey",blockHandler = "dealHandler_testHotKey")
public String testHotKey(@RequestParam(value = "p1", required = false) String p1,
@PathVariable(value = "p2", required = false) String p2){
return "------testHotKey"+ LocalDateTimeUtil.format(LocalDateTime.now(), "yyyy-MM-dd HH:mm:ss");
}
public String dealHandler_testHotKey(String p1, String p2, BlockException exception) {
return "-----dealHandler_testHotKey";
}
重启cloudalibaba-sentinel-service8401后,配置热点规则: 限流模式只支持QPS模式,@SentinelResource注解的方法参数索引,0代表第一个参数,1代表第二个参数,以此类推。 单机阀值以及统计窗口时长表示在此窗口时间超过阀值就限流。
测试热点,访问http://localhost:8401/testHotKey?p1=etrt ,超过1秒1次: 测试热点,访问http://localhost:8401/testHotKey?p1=etrt&p2=123 ,超过1秒1次:
测试热点,访问http://localhost:8401/testHotKey ,超过1秒1次:
8.1 参数例外项
我们期望p1参数当它是某个特殊值时,到达某个约定值后【普通正常限流】规则突然例外,不再限流。
点击编辑testHotKey热点规则例外: 测试热点,访问http://localhost:8401/testHotKey?p1=5 ,超过1秒1次:
9. 授权规则
在Sentinel的授权规则中,提供了白名单与黑名单两种授权类型。白放行、黑禁止。
9.1 使用黑名单
创建MyRequestOriginParser类和EmpowerController:
@RestController
@Slf4j
public class EmpowerController //Empower授权规则,用来处理请求的来源
{
@GetMapping(value = "/empower")
public String requestSentinel4(){
log.info("测试Sentinel授权规则empower");
return "Sentinel授权规则";
}
}
@Component
public class MyRequestOriginParser implements RequestOriginParser {
@Override
public String parseOrigin(HttpServletRequest httpServletRequest) {
return httpServletRequest.getParameter("serverName");
}
}
重启cloudalibaba-sentinel-service8401后,配置授权规则: 测试授权,访问http://localhost:8401/empower?serverName=test1 ,发现被禁止访问:
10. 规则持久化
一旦我们重启微服务应用,sentinel规则将消失,生产环境需要将配置规则进行持久化。将限流配置规则持久化进Nacos保存,只要刷新8401某个rest地址,sentinel控制台的流控规则就能看到,只要Nacos里面的配置不删除,针对8401上sentinel上的流控规则持续有效。
在pom中添加依赖:
<!--SpringCloud ailibaba sentinel-datasource-nacos -->
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-datasource-nacos</artifactId>
</dependency>
添加application.yml配置: 其中的flow表示流量控制规则,degrade表示熔断降级规则,authority访问控制规则,system系统保护规则,param-flow表示热点规则。可以配置多个规则,具体就是在配置一个数据源比如ds2。
配置Nachos页面,点击配置管理->创建配置: 将cloudalibaba-sentinel-service配置进去:
我们本次配置的是流量规则,配置的json文本说明:
[
{
"resource": "/rateLimit/byUrl",// 资源名称
"limitApp": "default", // 来源应用
"grade": 1, // 阈值类型,0表示线程数,1表示QPS;
"count": 1, // 单机阈值;
"strategy": 0, // 流控模式,0表示直接,1表示关联,2表示链路;
"controlBehavior": 0, //流控效果,0表示快速失败,1表示Warm Up,2表示排队等待;
"clusterMode": false // 是否集群
}
]
重启cloudalibaba-sentinel-service8401后,重新访问接口: 访问sentinel页面,发现流量规则并没有重启cloudalibaba-sentinel-service8401丢失:
11. OpenFeign和Sentinel整合
11.1 整合需求
- 使用OpenFeign中调用接口,都会写一个fallback服务降级方法,接口越来越多会导致代码膨胀不好管理。整合后希望通过fallback属性进行统一配置,feign接口里面定义的全部方法都走统一的服务降级,一个搞定即可。
- Sentinel访问触发了自定义的限流配置,在注解@SentinelResource里面配置的blockHandler方法。
11.2 改造cloudalibaba-provider-payment9001
添加pom.xml依赖:
<!--alibaba-sentinel-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
修改application.yml:
server:
port: 9001
spring:
application:
name: nacos-payment-provider
cloud:
nacos:
discovery:
server-addr: localhost:8848 #配置Nacos地址
sentinel:
transport:
dashboard: localhost:8080 #配置Sentinel dashboard控制台服务地址
port: 8719 #默认8719端口,假如被占用会自动从8719开始依次+1扫描,直至找到未被占用的端口
修改PayAlibabaController,添加getPayByOrderNo方法:
@GetMapping("/pay/nacos/get/{orderNo}")
@SentinelResource(value = "getPayByOrderNo",blockHandler = "handlerBlockHandler")
public ResultData getPayByOrderNo(@PathVariable("orderNo") String orderNo){
//模拟从数据库查询出数据并赋值给DTO
PayDTO payDTO = new PayDTO();
payDTO.setId(1024);
payDTO.setOrderNo(orderNo);
payDTO.setAmount(BigDecimal.valueOf(9.9));
payDTO.setPayNo("pay:"+IdUtil.fastUUID());
payDTO.setUserId(1);
return ResultData.success("查询返回值:"+payDTO);
}
public ResultData handlerBlockHandler(@PathVariable("orderNo") String orderNo,BlockException exception){
return ResultData.fail(ReturnCodeEnum.RC500.getCode(),"getPayByOrderNo服务不可用," +
"触发sentinel流控配置规则"+"\t"+"o(╥﹏╥)o");
}
11.3 改造cloud-api-commons
编写PayFeignSentinelApiFallBack和PayFeignSentinelApi类:
@FeignClient(value = "nacos-payment-provider",fallback = PayFeignSentinelApiFallBack.class)
public interface PayFeignSentinelApi {
@GetMapping("/pay/nacos/get/{orderNo}")
public ResultData getPayByOrderNo(@PathVariable("orderNo") String orderNo);
}
@Component
public class PayFeignSentinelApiFallBack implements PayFeignSentinelApi {
@Override
public ResultData getPayByOrderNo(String orderNo) {
return ResultData.fail(ReturnCodeEnum.RC500.getCode(),"对方服务宕机或不可用,FallBack服务降级o(╥﹏╥)o");
}
}
11.4 改造cloudalibaba-consumer-nacos-order83
添加pom.xml依赖:
<!--alibaba-sentinel-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
修改application.yml:
server:
port: 83
spring:
application:
name: nacos-order-consumer
cloud:
nacos:
discovery:
server-addr: localhost:8848
#消费者将要去访问的微服务名称(nacos微服务提供者叫什么你写什么)
service-url:
nacos-user-service: http://nacos-payment-provider
# 激活Sentinel对Feign的支持
feign:
sentinel:
enabled: true
修改主启动类Main83,添加@EnableFeignClients注解:
@EnableDiscoveryClient
@SpringBootApplication(scanBasePackages = "com.rocket.springcloud")
@EnableFeignClients(basePackages = "com.rocket.springcloud.common")
public class Main83 {
public static void main(String[] args) {
SpringApplication.run(Main83.class, args);
}
}
修改OrderNacosController类:
@Resource
private PayFeignSentinelApi payFeignSentinelApi;
@GetMapping(value = "/consumer/pay/nacos/get/{orderNo}")
public ResultData getPayByOrderNo(@PathVariable("orderNo") String orderNo) {
return payFeignSentinelApi.getPayByOrderNo(orderNo);
}
进行测试验证:先启动cloudalibaba-provider-payment9001,再启动cloudalibaba-consumer-nacos-order83,访问http://localhost:83/consumer/pay/nacos/get/2: 可见访问正常,配置Sentinel流控规则:
配置后再次疯狂访问http://localhost:83/consumer/pay/nacos/get/2 ,触发流控进行限流:
停掉cloudalibaba-provider-payment9001,再次疯狂访问http://localhost:83/consumer/pay/nacos/get/2 ,发现服务已经降级:
12. Gateway和Sentinel整合
创建子模块cloudalibaba-sentinel-gateway9528, 添加pom依赖:
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-transport-simple-http</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-spring-cloud-gateway-adapter</artifactId>
</dependency>
</dependencies>
创建启动类Main9528:
@SpringBootApplication
@EnableDiscoveryClient
public class Main9528 {
public static void main(String[] args) {
SpringApplication.run(Main9528.class,args);
}
}
请参考Sentinel整合Spring Cloud Gateway,在config包下面创建GatewayConfiguration类
@Configuration
public class GatewayConfiguration {
private final List<ViewResolver> viewResolvers;
private final ServerCodecConfigurer serverCodecConfigurer;
public GatewayConfiguration(ObjectProvider<List<ViewResolver>> viewResolversProvider,
ServerCodecConfigurer serverCodecConfigurer) {
this.viewResolvers = viewResolversProvider.getIfAvailable(Collections::emptyList);
this.serverCodecConfigurer = serverCodecConfigurer;
}
@Bean
@Order(Ordered.HIGHEST_PRECEDENCE)
public SentinelGatewayBlockExceptionHandler sentinelGatewayBlockExceptionHandler() {
// Register the block exception handler for Spring Cloud Gateway.
return new SentinelGatewayBlockExceptionHandler(viewResolvers, serverCodecConfigurer);
}
@Bean
@Order(-1)
public GlobalFilter sentinelGatewayFilter() {
return new SentinelGatewayFilter();
}
@PostConstruct
public void doInit() {
initGatewayRules();
}
//处理 自定义返回的例外信息
private void initGatewayRules() {
Set<GatewayFlowRule> rules = new HashSet<>();
rules.add(new GatewayFlowRule("pay_routh1").setCount(2).setIntervalSec(1));
GatewayRuleManager.loadRules(rules);
BlockRequestHandler handler = new BlockRequestHandler() {
@Override
public Mono<ServerResponse> handleRequest(ServerWebExchange exchange, Throwable t) {
Map<String, String> map = new HashMap<>();
map.put("errorCode", HttpStatus.TOO_MANY_REQUESTS.getReasonPhrase());
map.put("errorMessage", "请求太过频繁,系统忙不过来,触发限流(sentinel+gataway整合Case)");
return ServerResponse.status(HttpStatus.TOO_MANY_REQUESTS)
.contentType(MediaType.APPLICATION_JSON)
.body(BodyInserters.fromValue(map));
}
};
GatewayCallbackManager.setBlockHandler(handler);
}
}
配置application.yml:
server:
port: 9528
spring:
application:
name: cloudalibaba-sentinel-gateway # sentinel+gataway整合Case
cloud:
nacos:
discovery:
server-addr: localhost:8848
gateway:
routes:
- id: pay_routh1 #pay_routh1 #路由的ID(类似mysql主键ID),没有固定规则但要求唯一,建议配合服务名
uri: http://localhost:9001 #匹配后提供服务的路由地址
predicates:
- Path=/pay/** # 断言,路径相匹配的进行路由
依次启动cloudalibaba-provider-payment9001,cloudalibaba-sentinel-gateway9528测试访问http://localhost:9528/pay/nacos/3,发现正常访问: 疯狂访问发现被限流: