官方文档地址
以代码的方式实现在官方文档中都有详细的介绍,本小节主要就是以代码的方式实现流控规则和熔断降低规则。其他的可以参考官方文档。
Sentinel 可以简单的分为 Sentinel 核心库和 Dashboard。Dashboard就是Sentinel提供的web管理界面,核心库不依赖 Dashboard,但是结合 Dashboard 可以取得最好的效果。
Sentinel核心库就是我们java代码中需要引入的,我们可以不依靠Dashboard仅仅使用核心库来设置sentinel的一些规则。
使用 Sentinel 来进行资源保护,主要分为几个步骤:
先把可能需要保护的资源定义好(埋点),之后再配置规则。
也可以理解为,只要有了资源,我们就可以在任何时候灵活地定义各种流量控制规则。在编码的时候,只需要考虑这个代码是否需要保护,如果需要保护,就将之定义为一个资源。
第一步,引入maven坐标,如果单纯是使用sentinel核心库的话,是不需要依赖SpringCloud alibaba的,可以直接依赖spring-boot-starter-parent
因为它可以在分布式架构使用,并不一定是要在微服务架构中使用
<dependency>
<groupId>com.alibaba.cspgroupId>
<artifactId>sentinel-coreartifactId>
dependency>
第二步,创建controller层接口,并为该接口配置流控规则
sentinel所有的操作都是针对资源的,一个Rest接口其实就是对应的一个资源
package com.hs.springcloud.controller;
import com.alibaba.csp.sentinel.Entry;
import com.alibaba.csp.sentinel.SphU;
import com.alibaba.csp.sentinel.Tracer;
import com.alibaba.csp.sentinel.slots.block.BlockException;
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 lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.PostConstruct;
import java.util.ArrayList;
import java.util.List;
/**
* @Description:
* @Author 胡尚
* @Date: 2022/6/20 18:49
* @Version 1.0
*/
@Slf4j
@RestController
@RequestMapping("/demo")
public class HelloController {
// 定义一个资源名
private static final String RESOURCE_NAME = "hello";
/**
* 定义sentinel流控规则
* @return 。
*/
@RequestMapping("/hello")
public String hello(){
Entry entry = null;
try{
// 1. sentinel针对资源进行限制的,定义一个资源名称
entry = SphU.entry(RESOURCE_NAME);
// 被保护的业务逻辑
String str = "hello world";
log.info("-----{}-----", str);
return str;
} catch (BlockException e) {
// 资源访问阻止,被限流或被降级,进行相应的处理操作
log.info("block!");
return "被限流了!";
} catch (Exception ex){
// 若需要配置降级规则,需要通过这种方式记录业务异常
Tracer.traceEntry(ex, entry);
}finally {
// 务必保证 exit,务必保证每个 entry 与 exit 配对
if (entry != null){
entry.exit();
}
}
return null;
}
/**
* 定义流控规则
* PostConstruct注解是spring的init方法,当前Bean在spring容器中创建的时候就会自动调用被@PostConstruct注解修饰的方法
* 一个bean中可以存在多个方法被@PostConstruct注解修饰。
*/
@PostConstruct
private static void initFlowRules(){
// 流控规则,如果是要定义其他规则,那么list集合中就存储其他对象吗,具体参考官方文档
List<FlowRule> rules = new ArrayList<>();
//流控
FlowRule rule = new FlowRule();
// 设置受保护的资源,也就是要为哪个资源进行流控
rule.setResource(RESOURCE_NAME);
// 设置流控规则,限流阈值类型,QPS 模式(1)或并发线程数模式(0)
rule.setGrade(RuleConstant.FLOW_GRADE_QPS);
// 设置受保护的资源阀值,也就是说这里一秒只能请求一次该资源
rule.setCount(1);
// 将定义好的流控规则添加到集合中
rules.add(rule);
// 加载配置好的规则
FlowRuleManager.loadRules(rules);
}
}
Sentinel 支持通过 @SentinelResource
注解定义资源并配置 blockHandler
和 fallback
函数来进行限流之后的处理。示例:
// 原本的业务方法.
@SentinelResource(blockHandler = "blockHandlerForGetUser")
public User getUserById(String id) {
throw new RuntimeException("getUserById command failed");
}
// blockHandler 函数,原方法调用被限流/降级/系统保护的时候调用
public User blockHandlerForGetUser(String id, BlockException ex) {
return new User("admin");
}
注意 blockHandler
函数会在原方法被限流/降级/系统保护的时候调用,而 fallback
函数会针对所有类型的异常。请注意 blockHandler
和 fallback
函数的形式要求,更多指引可以参见 Sentinel 注解支持文档。
通过上一小节的案例,我们能够发现一个问题:Sentinel对接口中代码侵入性太强了,写起来非常麻烦,要写try{...}catch(..){...}
,哪些定义资源,处理流控后的业务逻辑代码都耦合在了代码中。
Sentinel提供了@SentinelResource
注解来改善这个问题。具体使用如下:
第一步,导入maven坐标
<dependency>
<groupId>com.alibaba.cspgroupId>
<artifactId>sentinel-annotation-aspectjartifactId>
dependency>
第二步,配置一个Bean
@Configuration
public class SentinelConfig {
@Bean
public SentinelResourceAspect sentinelResourceAspect() {
return new SentinelResourceAspect();
}
}
第三步,controller接口中使用@SentinelSource注解来定义资源
package com.hs.springcloud.controller;
import com.alibaba.csp.sentinel.Entry;
import com.alibaba.csp.sentinel.SphU;
import com.alibaba.csp.sentinel.Tracer;
import com.alibaba.csp.sentinel.annotation.SentinelResource;
import com.alibaba.csp.sentinel.slots.block.BlockException;
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 com.hs.springcloud.pojo.User;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.PostConstruct;
import java.util.ArrayList;
import java.util.List;
/**
* @Description:
* @Author 胡尚
* @Date: 2022/6/20 18:49
* @Version 1.0
*/
@Slf4j
@RestController
@RequestMapping("/demo")
public class HelloController {
private static final String RESOURCE_NAME = "hello";
private static final String USER_RESOURCE_NAME = "user";
/**
* 定义sentinel流控规则
*/
@RequestMapping("/hello")
public String hello(){
......
}
/**
* 使用@SentinelResource注解改善之前的写法
* value: 定义资源名,之前是需要在代码中使用Entry来定义资源
* blockHandler: 设置流控降级之后的处理方法,默认该方法必须声明在同一个类中
* blockHandlerClass: 如果流控降级之后的处理方法不在同一个类中就可以使用该参数来指定,
如果在其他类中该方法必须声明为static静态方法
* fallback:与blockHandler的区别是处理接口中所有的异常
* fallbackClass: 如果fallback的处理方法不在同一个类中,也可以使用该属性来指定,其他类中该方法必须声明为static静态方法
* exceptionsToIgnore: 排除某些异常,不让fallback 来进行处理
*/
@RequestMapping("/user")
@SentinelResource(value = USER_RESOURCE_NAME, blockHandler = "blockHandlerGorGetUser")
public User getUser(String id){
return new User("hushang");
}
/**
* 流控之后要执行的业务逻辑,该方法注意点:
* 1. 访问修饰符一定要是public
* 2. 返回值一定要和源方法保持一致
* 3. 形参一定要包含源方法的参数,并且顺序也一定要一样
* 4. 必须在参数最后加一个BlockException,通过该对象可以区分是什么规则的处理方法,流控/降级/系统规则......
*/
public User blockHandlerGorGetUser(String id, BlockException ex){
ex.printStackTrace();
return new User("流控了!");
}
/**
* 定义流控规则
* PostConstruct注解是spring的init方法,当前Bean在spring容器中创建的时候就会自动调用被@PostConstruct注解修饰的方法
*/
@PostConstruct
private static void initFlowRules(){
// 流控规则,如果是要定义其他规则,那么list集合中就存储其他对象吗,具体参考官方文档
List<FlowRule> rules = new ArrayList<>();
//流控
FlowRule rule = new FlowRule();
// 设置受保护的资源,也就是要为哪个资源进行流控
rule.setResource(RESOURCE_NAME);
// 设置流控规则,限流阈值类型,QPS 模式(1)或并发线程数模式(0)
rule.setGrade(RuleConstant.FLOW_GRADE_QPS);
// 设置受保护的资源阀值,也就是说这里一秒只能请求一次该资源
rule.setCount(1);
// 将定义好的流控规则添加到集合中
rules.add(rule);
// 通过@SentinelSource注解来定义资源并配置流控规则
FlowRule rule2 = new FlowRule();
rule2.setResource(USER_RESOURCE_NAME);
rule2.setGrade(RuleConstant.FLOW_GRADE_QPS);
rule2.setCount(1);
rules.add(rule2);
// 加载配置好的规则
FlowRuleManager.loadRules(rules);
}
}
若 blockHandler 和 fallback 都进行了配置,则被限流降级而抛出 BlockException
时只会进入 blockHandler
处理逻辑。也就是blockHandler 的优先级要高于fallback 。
熔断降级规则和流控规则的实现方式差不多,主要还是三个步骤:
熔断降级规则包含下面几个重要的属性:
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 引入) |
同一个资源可以同时有多个降级规则。
package com.hs.springcloud.controller;
import com.alibaba.csp.sentinel.Entry;
import com.alibaba.csp.sentinel.EntryType;
import com.alibaba.csp.sentinel.SphU;
import com.alibaba.csp.sentinel.Tracer;
import com.alibaba.csp.sentinel.annotation.SentinelResource;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.alibaba.csp.sentinel.slots.block.RuleConstant;
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRule;
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRuleManager;
import com.alibaba.csp.sentinel.slots.block.flow.FlowRule;
import com.alibaba.csp.sentinel.slots.block.flow.FlowRuleManager;
import com.hs.springcloud.pojo.User;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.PostConstruct;
import java.util.ArrayList;
import java.util.List;
/**
* @Description:
* @Author 胡尚
* @Date: 2022/6/20 18:49
* @Version 1.0
*/
@Slf4j
@RestController
@RequestMapping("/demo")
public class HelloController {
private static final String RESOURCE_NAME = "hello";
private static final String USER_RESOURCE_NAME = "user";
private static final String DEGRADE_RESOURCE_NAME = "degrade";
// 1. 定义资源
@RequestMapping("/degrade")
@SentinelResource(value = DEGRADE_RESOURCE_NAME, blockHandler = "blockHandlerForFb", entryType = EntryType.IN)
public User degradeTest(String id){
// 每一次调用都抛一个异常
throw new RuntimeException("出异常了");
}
// 熔断降级之后的调用方法
public User blockHandlerForFb(String id, BlockException ex){
return new User("熔断降级了!");
}
// 2. 定义规则
@PostConstruct
private void initDegradeRule() {
// 降级规则是 DegradeRule 类
List<DegradeRule> rules = new ArrayList<>();
DegradeRule rule = new DegradeRule();
// 设置资源名
rule.setResource(DEGRADE_RESOURCE_NAME);
// 熔断策略,这里是异常数策略 支持慢调用比例/异常比例/异常数策略
rule.setGrade(RuleConstant.DEGRADE_GRADE_EXCEPTION_COUNT);
// 熔断触发的条件是出现两次异常
rule.setCount(2);
// 熔断触发的最小请求数
rule.setMinRequestAmount(3);
// 统计时长 单位是毫秒 默认1000毫秒
rule.setStatIntervalMs(60 * 1000);
// 上面这三个条件的的意思是,在一分钟之内,3次请求中如果有两次异常就触发熔断
// 熔断时长 单位是秒,10秒后会解除熔断,如果10秒后的第一次请求就发生了异常,那么就立刻熔断。
rule.setTimeWindow(10);
rules.add(rule);
DegradeRuleManager.loadRules(rules);
}
}
下一篇:Sentinel部署、微服务使用sentinel整合springcloud alibaba、sentinel控制台详解