目录
一、持久化方式
二、持久化步骤
第一步:引入依赖
第二步:编辑配置文件
第三步:在nacos中配置规则
三、持久化原理
1、持久化接口
2、抽象类
3、案例操作
一、持久化方式
直接使用dashboard和sentinel配置各种规则时,默认是存在了内存直接使用dashboard和sentinel配置各种规则时,默认是存在了内存中。如果服务器重启那么数据就会丢失,从而Sentinel提供了5中持久化的方式,将各种配置数据进行持久化,若服务器重启就重新加载持久化的数据,防止数据丢失。
目前 Sentinel 中默认实现了5种规则持久化的方式,分别是:file、redis、nacos、zk和apollo。 可以在 sentinel 控制台中编辑 限流配置,并且同步到 nacos 做持久化。在 nacos 中修改了限流配置,也可以同步到 sentinel 控制台。
二、持久化步骤
对sentinel中的配置进行持久化需要三步:
第一步:引入依赖
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
<version>2.2.1.RELEASE</version>
</dependency>
<!-- 引入 sentinel 持久化到 nacos 中的依赖 -->
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-datasource-nacos</artifactId>
<version>1.7.2</version>
</dependency>
第二步:编辑配置文件
server:
port: 9000
spring:
application:
name: ORDER-SERVICE-COSTUMER
cloud:
#配置 nacos server 注册中心总的地址
nacos:
server-addr: localhost:8848 # 配置nacos server 注册中心地址
sentinel:
enabled: true #开启sentinel保护
transport:
dashboard: localhost:8080 #配置连接到 dashboard 的web的地址
port: 8719 #指定sentinel组件与 sentinel dashboard的TCP通信地址192.168.56.21:8719
datasource:
ds1: # 这个名字任意起
naccs:
server-addr: localhost:8848 # 配置nacos server 注册中心地址
dataId: ${spring.application.name} # 和nacos中保持对应
groupId: DEFAULT_GROUP # 和 nacos中保持对应
data-type: json # 规则类型:流控
rule-type: flow # 对于 nacos 数据类型
第三步:在nacos中配置规则
以资源进行限流为案例。
[
{
"resource": "/order/buy/{id}", #保护的资源
"limitApp": "default", #来源应用
"grade": 0, #阈值线程数 0表示阈值,1表示线程数
"count": 1, # 单机阈值
"strategy": 0, # 流控模式 0表示直接 1表示关联 2表示链路
"controlBehavior": 0, #0表示快速失败 1表示预热 2表示排队等待
"clusterMode": false #是否是集群
}
]
对应 sentinel中的规则如下图
三、持久化原理
1、持久化接口
Sentinel 为我们提供了两个接口来实现规则的持久化,他们分别是:ReadableDataSource 和 WritableDataSource。其中主要关注ReadableDataSource。
public interface ReadableDataSource<S, T> {
//将原始数据转换成我们所需的格式
T loadConfig() throws Exception;
//从数据源中读取原始的数据
S readSource() throws Exception;
//获取该种数据源的SentinelProperty对象,数据变化监听器
SentinelProperty<T> getProperty();
}
2、抽象类
Sentinel 还为我们提供了一个抽象类:AbstractDataSource,该抽象类中实现了两个方法,具体的数据源实现类只需要实现一个 readSource 方法即可。
public abstract class AbstractDataSource<S, T> implements ReadableDataSource<S, T> {
// Converter接口负责转换数据
protected final Converter<S, T> parser;
//SentinelProperty接口负责触发PropertyListener的configUpdate方法的回调
protected final SentinelProperty<T> property;
public AbstractDataSource(Converter<S, T> parser) {
if (parser == null) {
throw new IllegalArgumentException("parser can't be null");
}
this.parser = parser;
this.property = new DynamicSentinelProperty<T>();
}
@Override
public T loadConfig() throws Exception {
return loadConfig(readSource());
}
public T loadConfig(S conf) throws Exception {
T value = parser.convert(conf);
return value;
}
@Override
public SentinelProperty<T> getProperty() {
return property;
}
}
实际上每个具体的DataSource主要要做三件事情:
- 实现 readSource 方法将数据源中的原始数据转换成我们可以处理的数据S
- 提供一个 Converter 来将数据S转换成最终的数据T
- 将最终的数据T更新到具体的 RuleManager 中去。
3、案例操作
以Redis做持久化数据源为例。
public RedisDataSource(RedisConnectionConfig connectionConfig, String ruleKey, String channel,
Converter<String, T> parser) {
super(parser);
this.redisClient = getRedisClient(connectionConfig);
this.ruleKey = ruleKey;
//从Redis中加载原数据
loadInitialConfig();
}
private void loadInitialConfig() {
try {
//调用AbstractDatasource的方法,父类会调用readSource方法获得原数据
T newValue = loadConfig();
//将加载的到的数据,通过监听器更新规则的数据
getProperty().updateValue(newValue);
} catch (Exception ex) {
RecordLog.warn("[RedisDataSource] Error when loading initial config", ex);
}
}
@Override
public String readSource() {
RedisCommands<String, String> stringRedisCommands = redisClient.connect().sync();
return stringRedisCommands.get(ruleKey);
}