1、Sentinel 简介
Spring Cloud Alibaba Sentinel 是一个开源的流量控制和熔断框架,是Spring Cloud生态系统中的一个组件。Sentinel的主要功能是实时监控微服务应用的流量,包括入站和出站的流量,并提供流量控制、熔断降级、系统负载保护和热点参数保护等功能。它可以帮助开发者快速识别和处理应用程序中的故障和异常,并保障应用程序的稳定性和可靠性。
2、Sentinel 简单使用
2.1、实现限流功能
Sentinel的使用主要分为以下4步:
① 添加 Sentinel 依赖
② 定义资源
③ 定义规则
④ 验证效果
这里使用简单的maven工程来使用Sentinel。
① 添加依赖
首先我们要创建一个maven工程,勾选Spring Web跟Spring Cloud Alibaba Sentinel这两个依赖
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
② 定义资源
这里有两种定义资源的方式
方法一:通过代码定义
方法二:通过注解定义(基本使用的是这一种)
创建controller文件,这里我创建的是UserController,里面定义了两个方法:getId() 和getName()。分别用两种方式来定义资源
import com.alibaba.csp.sentinel.Entry;
import com.alibaba.csp.sentinel.SphU;
import com.alibaba.csp.sentinel.annotation.SentinelResource;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import org.springframework.cglib.core.Block;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.Random;
@RestController
@RequestMapping("/user")
public class UserController {
// 方法一
@RequestMapping("/getid")
public String getId(){
try (Entry entry= SphU.entry("getid")){ // 定义资源
// 正常执行的业务逻辑
return "Id:"+new Random().nextInt(100);
}catch (BlockException blockException){
// 如果执行到此处,说明当前资源已经被限流或熔断
return "被限制";
}
}
// 方法二 注解定义资源
@SentinelResource(value = "getname",
blockHandler = "myBlockHander")
@RequestMapping("/getname")
public String getName(){
return "Name:"+new Random().nextInt(100);
}
// 限流之后执行的方法
public String myBlockHander(BlockException blockException){
return "被限制了";
}
}
@SentinelResource 注解属性说明:
value:自定义要设置的资源名称,必需项(不能为空)
entryType:资源调用的流量类型:入口流量(EntryType.IN)和出口流量(EntryType.OUT),注意系统规则只对 IN 生效。
blockHandler/blockHandlerClass:限流和熔断时执行 BlockException 所对应的方法名。(以上面的案例来说调用getName方法出现问题,被限流之后,执行的是myBlockHander方法。)fallback/fallbackClass:非 BlockException 时,其他非限流、非熔断时异常对应的方法。
exceptionsToIgnore:用于指定哪些异常被排除掉,不会计入异常统计中,也不会进入 fallback 逻辑中,而是会原样抛出。注意事项:
1、1.6.0 之前的版本 fallback 函数只针对熔断降级异常(DegradeException)进行处理,不能针对业务异常进行处理。
2、blockHandler使用注意:
(1)blockHandler 返回方法的类型必须要跟限流的原方法返回类型一样
(2)blockHandler 方法必须要有 BlockException 异常参数
(3)blockHandler 方法参数必须跟限流原方法参数一样
③ 定义规则
在启动类里添加定义规则的方法,这里只对 getId方法进行限流。
将flowRule.setResource("getid")里的 “getid” 修改成 “getname” 就变成对getName方法进行限流。
import com.alibaba.csp.sentinel.slots.block.RuleConstant;
import com.alibaba.csp.sentinel.slots.block.flow.FlowRule;
import com.alibaba.csp.sentinel.slots.block.flow.FlowRuleManager;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import java.util.ArrayList;
import java.util.List;
@SpringBootApplication
public class SentinelDemoApplication {
public static void main(String[] args) {
SpringApplication.run(SentinelDemoApplication.class, args);
// 启动的时候执行限流规则
initFlowRules();
}
// 定义限流规则
private static void initFlowRules(){
List<FlowRule> rules=new ArrayList<>();
System.out.println("cwx真帅");
// 定义一个限流规则
FlowRule flowRule=new FlowRule();
flowRule.setResource("getid"); // 资源名|必须参数
flowRule.setGrade(RuleConstant.FLOW_GRADE_QPS); // 限流指标:QPS/线程数 |必须参数
flowRule.setCount(1); // 限流数量(上一步 QPS 或线程数的值) |必须参数
flowRule.setStrategy(RuleConstant.STRATEGY_DIRECT); //调用关系限流策略【非必须设置】
flowRule.setControlBehavior(RuleConstant.CONTROL_BEHAVIOR_DEFAULT); // 流控效果【非必须设置】
flowRule.setClusterMode(false); // 是否集群限流【非必须设置,默认非集群】
rules.add(flowRule);
FlowRuleManager.loadRules(rules);
}
}
方法参数解析
setStrategy:设置调用关系限流策略,包含的值有:
直接(RuleConstant.STRATEGY_DIRECT)【默认值】
链路(RuleConstant.STRATEGY_RELATE)
关联(RuleConstant.STRATEGY CHAIN)。
setControlBehavior:设置流控效果,包含的值有:
直接拒绝(RuleConstant.CONTROL BEHAVIOR_DEFAULT)【默认值】冷启动(RuleConstant.CONTROL BEHAVIOR WARM UP)
匀速启动(RuleConstant.CONTROL_BEHAVIOR_RATE_LIMITER)
冷启动+匀速启动(RuleConstant.CONTROL BEHAVIOR WARM UP RATE LIMITER)
④ 验证效果
启动应用程序,连续访问 getid服务。(将flowRule.setResource("getid")里的 “getid” 修改成 “getname” 就变成对getName方法进行限流。)
localhost:8080/user/getid
2.2、实现熔断(降级)功能
熔断(降级)功能的实现跟限流类似,唯一不同的是规则不同。也就是只需要调整第③步的定义规则。
在启动类上添加熔断的定义规则方法
// 设置熔断(降级)规则
private static void initDegradeRules()
{
List<DegradeRule> degradeRules=new ArrayList<>();
// 设置一条熔断规则
DegradeRule degradeRule=new DegradeRule();
degradeRule.setResource("getname"); //熔断的资源名
// // 熔断类型:【慢调用】、异常比例、异常个数
degradeRule.setGrade(RuleConstant.DEGRADE_GRADE_RT);
// 熔断类型的值,如果是慢调用就是慢调用的值,单位是毫秒; 异常比例 / 异常个数 模式下为对应的值
degradeRule.setCount(10);
degradeRule.setStatIntervalMs(1000); // 熔断的统计时间,单位是毫秒
degradeRule.setSlowRatioThreshold(0.5); // 慢调用比例,此值是 0~1 之间的小数
// 熔断的最小请求数,请求数小于该值时,及时异常比例超过阈值也不会熔断
degradeRule.setMinRequestAmount(1);
degradeRule.setTimeWindow(5); // 熔断的时长,时间单位是秒
degradeRules.add(degradeRule);
DegradeRuleManager.loadRules(degradeRules);
}
这里设置的意思是:选择 慢调用 的模式,在1秒内有超过50%的请求的请求时长超过10毫秒 且 请求数目超过1个,触发熔断,熔断时长为5秒。
方法说明:
resoure:资源名,即规则的作用对象。
grade:熔断策略,分别有 慢调用比例、异常比例、异常数策略。
count:慢调用比例模式下为慢调用临界RT(超出该值即为慢调用);异常比例/异常数模式下为对应的阈值。
timeWindow:熔断时长,单位为 秒/s。
minRequestAmount:熔断触发的最小请求数,请求数小于该值时,即使异常比例超出阈值也不会熔断(1.7.0 引入)
statIntervalMs:统计时长(单位为毫秒/ms),如60*1000代表分钟级(1.8.0引入)
slowRatioThreshold:慢调用比例阈值,仅慢调用比例模式有效(1.8.0引入)
注:熔断(降级)也是会触发@SentinelResource中的 blockHandler 方法,只不过他和限流抛出的 BlockException 不同。
在main方法中调用该方法,熔断跟限流是可以同时实现的,看哪个先执行,就显示哪个的效果。
public static void main(String[] args) {
SpringApplication.run(SentinelDemoApplication.class, args);
initFlowRules(); // 启动的时候执行限流规则
initDegradeRules(); // 启动的时候执行熔断规则
}
修改controller
import com.alibaba.csp.sentinel.Entry;
import com.alibaba.csp.sentinel.SphU;
import com.alibaba.csp.sentinel.annotation.SentinelResource;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeException;
import com.alibaba.csp.sentinel.slots.block.flow.FlowException;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.Random;
@RestController
@RequestMapping("/user")
public class UserController {
// 方法一
@RequestMapping("/getid")
public String getId(){
try (Entry entry= SphU.entry("getid")){ // 定义资源
// 正常执行的业务逻辑
return "Id:"+new Random().nextInt(100);
}catch (BlockException blockException){
// 如果执行到此处,说明当前资源已经被限流或熔断
return "被限制";
}
}
// 方法二
@SentinelResource(value = "getname",
blockHandler = "myBlockHander")
@RequestMapping("/getname")
public String getName() throws InterruptedException {
Thread.sleep(100);
return "Name:"+new Random().nextInt(100);
}
// 限流或者熔断之后执行的方法
public String myBlockHander(BlockException blockException){
if(blockException instanceof FlowException){
// 限流异常
return "您被限流了";
}else if(blockException instanceof DegradeException){
// 熔断异常
return "您被熔断了";
}
return "被限制了";
}
}
访问 localhost:8080/user/getname