Sentinel限流熔断工具的使用以及持久化
最近公司交付了一项任务,使用阿里巴巴组件sentinel进行限流和熔断,前后经历了几天的开发和部署,最终成功上线,途中也踩了一些坑,查了不少资料,会在文章中写明解决的办法,方便大家一次过。
sentinel部署
添加依赖
这部分比较简单,我的项目是springboot,直接在pom.xml文件中添加依赖。
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
这里遭遇了第一个坑,就是依赖引不进来,通过查阅资料,在pom.xml再次添加以下代码,成功引入依赖。
<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>2.2.3.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
这里大家注意下,这个version的版本2.2.3里面有sentinel的版本号,含义是客户端的版本号,其中客户端版本号要跟dashbord版本号相近,不然会出现其他问题,关于版本不一致的坑我等会再说。
添加配置
依赖引入之后,我们在application.properties这个配置文件中增加两行配置。
spring.cloud.sentinel.transport.dashboard=127.0.0.1:9192
spring.cloud.sentinel.enabled=true
第一个配置是自己的控制台的地址,后面的端口是自己配置的,我用的9192,默认8080
运行客户端
我们要去官方下载控制台的jar文件,下载地址。
注意版本要跟前面的客户端版本接近,我当时用的最新版本1.8.5。
下载好了之后,开始在本地用java命令启动。
java -jar -Dserver.port=9192 sentinel-dashboard-1.8.5.jar
运行成功之后,就可以打开客户端了。
账号和密码都是sentinel
注意客户端是懒加载,如果没有任何东西显示,你随便运行一个接口,就能看到了。
点击列表视图,更方便的管理接口。
至于每个功能是做什么用的,我这里就不赘述了,你们可以看其他文档了解,我只教大家怎么成功的部署起来。
这里教大家怎么用这个东西
版本不一致带来的坑
通过设置上面的流控和熔断,你会发现自己的配置生效了,如果你发现你的配置无法生效,特别是熔断的阈值,设置了小数,但实际没有生效,那就恭喜你步入了第二个坑;如果生效成功,这段直接跳过。
不生效就是上面说的,版本不一致,导致的BUG,解决版本就是版本对应好,我之前客户端版本是1.6.3,所以没生效。
不能持久化
第三个坑来了,你会发现,这个工具是基于内存的,每次服务重启后,你配置的规则就会消失,这还玩个der!
那么,有什么方法,能够让服务重启后,还能保持原来的配置呢?当然有!
网上的攻略提供了两个持久化的方法
1.配置保留在服务器本地文件,随时读写。
2.使用nacos读写配置。
如果你的项目本身就是nacos做配置中心,那很简单,用nacos,网上攻略很多,我不赘述了。
我要讲的是写本地的配置,这种适用于那种不是nacos配置的服务。
sentinel持久化
添加配置
<!--sentinel持久化pull模式到本地文件-->
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-datasource-extension</artifactId>
</dependency>
添加service文件
这个文件不需要加@service注解,可以直接用,ruleDir是文件的读写位置,可以修改为你喜欢的地址。
import com.alibaba.csp.sentinel.command.handler.ModifyParamFlowRulesCommandHandler;
import com.alibaba.csp.sentinel.datasource.*;
import com.alibaba.csp.sentinel.init.InitFunc;
import com.alibaba.csp.sentinel.slots.block.authority.AuthorityRule;
import com.alibaba.csp.sentinel.slots.block.authority.AuthorityRuleManager;
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.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowRule;
import com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowRuleManager;
import com.alibaba.csp.sentinel.slots.system.SystemRule;
import com.alibaba.csp.sentinel.slots.system.SystemRuleManager;
import com.alibaba.csp.sentinel.transport.util.WritableDataSourceRegistry;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import java.io.File;
import java.io.IOException;
import java.util.List;
public class FileDataSourceInit implements InitFunc {
@Override
public void init() throws Exception {
String ruleDir = "/opt/sentinel/rules";
// String ruleDir = System.getProperty("user.home") + "/sentinel/rules";
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 hotParamFlowRulePath = ruleDir + "/param-flow-rule.json";
this.mkdirIfNotExits(ruleDir);
this.createFileIfNotExits(flowRulePath);
this.createFileIfNotExits(degradeRulePath);
this.createFileIfNotExits(systemRulePath);
this.createFileIfNotExits(authorityRulePath);
this.createFileIfNotExits(hotParamFlowRulePath);
// 流控规则
ReadableDataSource<String, List<FlowRule>> flowRuleRDS = new FileRefreshableDataSource<>(
flowRulePath,
flowRuleListParser
);
// 将可读数据源注册至FlowRuleManager
// 这样当规则文件发生变化时,就会更新规则到内存
FlowRuleManager.register2Property(flowRuleRDS.getProperty());
WritableDataSource<List<FlowRule>> flowRuleWDS = new FileWritableDataSource<>(
flowRulePath,
this::encodeJson
);
// 将可写数据源注册至transport模块的WritableDataSourceRegistry中
// 这样收到控制台推送的规则时,Sentinel会先更新到内存,然后将规则写入到文件中
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<>(
flowRulePath,
authorityRuleListParser
);
AuthorityRuleManager.register2Property(authorityRuleRDS.getProperty());
WritableDataSource<List<AuthorityRule>> authorityRuleWDS = new FileWritableDataSource<>(
authorityRulePath,
this::encodeJson
);
WritableDataSourceRegistry.registerAuthorityDataSource(authorityRuleWDS);
// 热点参数规则
ReadableDataSource<String, List<ParamFlowRule>> hotParamFlowRuleRDS = new FileRefreshableDataSource<>(
hotParamFlowRulePath,
hotParamFlowRuleListParser
);
ParamFlowRuleManager.register2Property(hotParamFlowRuleRDS.getProperty());
WritableDataSource<List<ParamFlowRule>> paramFlowRuleWDS = new FileWritableDataSource<>(
hotParamFlowRulePath,
this::encodeJson
);
ModifyParamFlowRulesCommandHandler.setWritableDataSource(paramFlowRuleWDS);
}
/**
* 流控规则对象转换
*/
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>> hotParamFlowRuleListParser = source -> JSON.parseObject(
source,
new TypeReference<List<ParamFlowRule>>() {
}
);
/**
* 创建目录
*
* @param filePath
*/
private void mkdirIfNotExits(String filePath) {
File file = new File(filePath);
if (!file.exists()) {
file.mkdirs();
}
}
/**
* 创建文件
*
* @param filePath
* @throws IOException
*/
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);
}
}
添加引用
在项目的resource文件夹下面添加这样一个文件
注意!!!
这个文件夹和文件的名字都不能有任何错误!否则就会失败,文件名的内容是上面那个类的地址。
之前我就是因为看了别人的攻略 把META-INF写成了META_INF,半天不生效,气死了!
文件名是com.alibaba.csp.sentinel.init.InitFunc 没有任何后缀。
注意
还需要注意的地方是,你的项目是否引用到这个resource了,不然引用不到同样会失败。
一般有这样的配置,说明引用到resource了。
总结
做完这么多,持久化就成功了,你从控制台修改配置,能够直接写入到文件中。
文件格式是这样的。
你直接修改文件,很快控制台也能读到,并且立刻生效。
并且重启服务,配置还是在的,说明持久化生效了。
还有需要注意的是,有些运维部署这个项目,每次重启后,这个文件夹地址会刷新,导致文件不见了,有一种做法是添加映射卷,将实体文件夹映射到服务器文件夹中,这样每次这个文件夹不会刷新,会读取实体文件夹,具体做法我不会,是我们公司运维操作的。
攻略暂时就做到这里,有任何不懂的可以在评论区留下您的意见。