Spring Cloud Alibaba - Sentinel入门案例(六)(补充使用 (一) Sentinel规则持久化 / @SentinelResource的使用 )
回溯
前面的博文已经讲述了关于Sentinel的简单使用,但是还是有一些需要功能点没有覆盖,这边在进行简单分析。
正文
Sentinel规则持久化
在之前的博文中,有一个遗留的问题,就是无论设定了什么规则,当项目重启的时候,规则就消失了,需要重新配置,这是因为配置的规则并没有持久化文件,而是存在内存中,所以当项目启动的时候之前配置的都会消失。
本地文件数据源会定时轮询文件的变更,读取规则。这样我们既可以在应用本地直接修改文件来更新规则,也可以通过 Sentinel 控制台推送规则。
首先需要新增一个配置类,配置本地持久化文件的存放路径等等。
public class FilePersistence implements InitFunc {
@Value("spring.application:name")
private String appcationName;
private Converter<String, List<FlowRule>> flowRuleListParser = source ->
JSON.parseObject(
source,
new TypeReference<List<FlowRule>>() {
}
);
private Converter<String, List<DegradeRule>> degradeRuleListParser = source
-> JSON.parseObject(
source,
new TypeReference<List<DegradeRule>>() {
}
);
private Converter<String, List<SystemRule>> systemRuleListParser = source ->
JSON.parseObject(
source,
new TypeReference<List<SystemRule>>() {
}
);
private Converter<String, List<AuthorityRule>> authorityRuleListParser =
source -> JSON.parseObject(
source,
new TypeReference<List<AuthorityRule>>() {
}
);
private Converter<String, List<ParamFlowRule>> paramFlowRuleListParser =
source -> JSON.parseObject(
source,
new TypeReference<List<ParamFlowRule>>() {
}
);
@Override
public void init() throws Exception {
String ruleDir = System.getProperty("user.home") + "/sentinelrules/" + appcationName;
String flowRulePath = ruleDir + "/flow-rule.json";
String degradeRulePath = ruleDir + "/degrade-rule.json";
String systemRulePath = ruleDir + "/system-rule.json";
String authorityRulePath = ruleDir + "/authority-rule.json";
String paramFlowRulePath = ruleDir + "/param-flow-rule.json";
this.mkdirIfNotExits(ruleDir);
this.createFileIfNotExits(flowRulePath);
this.createFileIfNotExits(degradeRulePath);
this.createFileIfNotExits(systemRulePath);
this.createFileIfNotExits(authorityRulePath);
this.createFileIfNotExits(paramFlowRulePath);
// 流控规则
ReadableDataSource<String, List<FlowRule>> flowRuleRDS = new
FileRefreshableDataSource<>(
flowRulePath,
flowRuleListParser
);
FlowRuleManager.register2Property(flowRuleRDS.getProperty());
WritableDataSource<List<FlowRule>> flowRuleWDS = new
FileWritableDataSource<>(
flowRulePath,
this::encodeJson
);
WritableDataSourceRegistry.registerFlowDataSource(flowRuleWDS);
// 降级规则
ReadableDataSource<String, List<DegradeRule>> degradeRuleRDS = new
FileRefreshableDataSource<>(
degradeRulePath,
degradeRuleListParser
);
DegradeRuleManager.register2Property(degradeRuleRDS.getProperty());
WritableDataSource<List<DegradeRule>> degradeRuleWDS = new
FileWritableDataSource<>(
degradeRulePath,
this::encodeJson
);
WritableDataSourceRegistry.registerDegradeDataSource(degradeRuleWDS);
// 系统规则
ReadableDataSource<String, List<SystemRule>> systemRuleRDS = new
FileRefreshableDataSource<>(
systemRulePath,
systemRuleListParser
);
SystemRuleManager.register2Property(systemRuleRDS.getProperty());
WritableDataSource<List<SystemRule>> systemRuleWDS = new
FileWritableDataSource<>(
systemRulePath,
this::encodeJson
);
WritableDataSourceRegistry.registerSystemDataSource(systemRuleWDS);
// 授权规则
ReadableDataSource<String, List<AuthorityRule>> authorityRuleRDS = new
FileRefreshableDataSource<>(
authorityRulePath,
authorityRuleListParser
);
AuthorityRuleManager.register2Property(authorityRuleRDS.getProperty());
WritableDataSource<List<AuthorityRule>> authorityRuleWDS = new
FileWritableDataSource<>(
authorityRulePath,
this::encodeJson
);
WritableDataSourceRegistry.registerAuthorityDataSource(authorityRuleWDS);
// 热点参数规则
ReadableDataSource<String, List<ParamFlowRule>> paramFlowRuleRDS = new
FileRefreshableDataSource<>(
paramFlowRulePath,
paramFlowRuleListParser
);
ParamFlowRuleManager.register2Property(paramFlowRuleRDS.getProperty());
WritableDataSource<List<ParamFlowRule>> paramFlowRuleWDS = new
FileWritableDataSource<>(
paramFlowRulePath,
this::encodeJson
);
ModifyParamFlowRulesCommandHandler.setWritableDataSource(paramFlowRuleWDS);
}
private void mkdirIfNotExits(String filePath) {
File file = new File(filePath);
if (!file.exists()) {
file.mkdirs();
}
}
private void createFileIfNotExits(String filePath) throws IOException {
File file = new File(filePath);
if (!file.exists()) {
file.createNewFile();
}
}
private <T> String encodeJson(T t) {
return JSON.toJSONString(t);
}
}
添加配置
在resources下创建配置目录 META-INF/services ,然后添加文件
com.alibaba.csp.sentinel.init.InitFunc
里面存放刚才编写的配置类的路径:
例如:com.example.nacos.demo.config.FilePersistence
配置完毕,重启项目,这时候就发现,设置的规则,之后重启都可以加载,证明持久化成功。
ps(sentinel是懒加载,所以重启项目,还是需要访问一下,之前的规则才能重新展示)
@SentinelResource的使用
在定义了资源点之后,我们可以通过Dashboard来设置限流和降级策略来对资源点进行保护。同时还能通过@SentinelResource来指定出现异常时的处理策略。
@SentinelResource 用于定义资源,并提供可选的异常处理和 fallback 配置项。其主要参数如下:
属性 | 作用 |
---|---|
value | 资源名称 |
entryType | entry类型,标记流量的方向,取值IN/OUT,默认是OUT |
blockHandler | 处理BlockException的函数名称,函数要求:1. 必须是 public 2.返回类型 参数与原方法一致 3. 默认需和原方法在同一个类中。若希望使用其他类的函数,可配置blockHandlerClass ,并指定blockHandlerClass里面的方法。 |
blockHandlerClass | 存放blockHandler的类,对应的处理函数必须static修饰。 |
fallback | 用于在抛出异常的时候提供fallback处理逻辑。fallback函数可以针对所有类型的异常(除了 exceptionsToIgnore 里面排除掉的异常类型)进行处理。函数要求:1. 返回类型与原方法一致 2. 参数类型需要和原方法相匹配 3. 默认需和原方法在同一个类中。若希望使用其他类的函数,可配置fallbackClass ,并指定fallbackClass里面的方法。 |
fallbackClass | 存放fallback的类。对应的处理函数必须static修饰。 |
defaultFallback | 用于通用的 fallback 逻辑。默认fallback函数可以针对所有类型的异常进行处理。若同时配置了 fallback 和 defaultFallback,以fallback为准。函数要求:1. 返回类型与原方法一致 2. 方法参数列表为空,或者有一个 Throwable 类型的参数。3. 默认需要和原方法在同一个类中。若希望使用其他类的函数,可配置fallbackClass ,并指定 fallbackClass 里面的方法。 |
exceptionsToIgnore | 指定排除掉哪些异常。排除的异常不会计入异常统计,也不会进入fallback逻辑,而是原样抛出。 |
exceptionsToTrace | 需要trace的异常 |
定义限流和降级后的处理方法
方式一
把方法定义在资源类中
例如:
@Service
@Slf4j
public class MessageServiceImpl implements MessageService {
int throwableCount = 0;
@Override
@SentinelResource(value = "message", blockHandler = "blockHandler", fallback = "fallback")
public String message() {
throwableCount++;
if (throwableCount % 3 == 0) {
throw new RuntimeException();
}
return "success";
}
//BlockException时进入的方法
public String blockHandler(BlockException ex) {
log.error("{}", ex);
return "接口被限流或者降级了...";
}
//Throwable时进入的方法
public String fallback(Throwable throwable) {
log.error("{}", throwable);
return "接口发生异常了...";
}
}
在sentinel控制台设置一个简单的流控规则。
请求后,根据控制台的日志,证明了,当发生熔断和异常的时候有进入到对应的方法里面。
方式二
将限流和降级方法外置到单独的类中。
@Slf4j
public class MessageServiceBlockHandlerClass {
//注意这里必须使用static修饰方法
public static String blockHandler(BlockException ex) {
log.error("{}", ex);
return "接口被限流或者降级了...";
}
}
@Slf4j
public class MessageServiceFallbackClass {
//注意这里必须使用static修饰方法
public static String fallback(Throwable throwable) {
log.error("{}", throwable);
return "接口发生异常了...";
}
}
@Service
@Slf4j
public class MessageServiceImpl implements MessageService {
int throwableCount = 0;
@Override
@SentinelResource(value = "message", blockHandlerClass = MessageServiceBlockHandlerClass.class,blockHandler = "blockHandler", fallbackClass = MessageServiceFallbackClass.class,fallback = "fallback")
public String message() {
throwableCount++;
if (throwableCount % 3 == 0) {
throw new RuntimeException();
}
return "success";
}
}
测试步骤和上面一致。