sentinel源码分析: dashboard与微服务的交互、pull模式持久化

文章目录

    • 原始方式
      • 微服务端规则如何保存
      • 规则如何加载进内存
      • 微服务端接收控制台请求
      • 控制台推送规则
      • 总结
    • pull拉模式
      • 官方demo
      • 如何整合Spring Cloud
      • 整合Spring Cloud



前置知识 @SentinelResource的实现原理、SphU.entry()方法中ProcessorSlotChain链、entry.exit()

建议先会使用sentinel,并对底层实现有一些理解再来看sentinel规则持久化。当然你直接看也能看懂。



Sentinel规则的推送有下面三种模式:

推送模式说明优点缺点
原始模式API 将规则推送至客户端并直接更新到内存中,扩展写数据源 WritableDataSource简单,无任何依赖不保证一致性;规则保存 在内存中,重启即消失。 严重不建议用于生产环境
Pull模式客户端主 动向某个规则管理中心定期轮询拉取规 则,这个规则中心可以是 DB、文件等。扩展写数据源 WritableDataSource简单,无任何依赖; 规则持久化不保证一致性;实时性不 保证,拉取过于频繁也可 能会有性能问题。
Push模式规则中心 统一推送,客户端通过注册监听器的方 式时刻监听变化,比如使用 Nacos、 Zookeeper 等配置中心。这种方式有 更好的实时性和一致性保证。生产环境 下一般采用 push 模式的数据源。扩展读数据源 ReadableDataSource规则持久化;一致 性;快速引入第三方依赖



原始方式

微服务与控制台通信流程 在线流程图

在这里插入图片描述



如果不做任何修改,Dashboard 的推送规则方式是通过 API 将规则推送至客户端 并直接更新到内存中:

在这里插入图片描述



微服务端规则如何保存

我们先来看看我们微服务这边规则是如何保存的。就以流控为例

在FlowSlot类的entry()方法中,它会先取出当前资源所有流控规则

// 取当前资源所有流控规则
Collection<FlowRule> rules = ruleProvider.apply(resource.getName());if (rules != null) {for (FlowRule rule : rules) {// 校验流控if (!canPassCheck(rule, context, node, count, prioritized)) {throw new FlowException(rule.getLimitApp(), rule);}}
}// 继续更近ruleProvider.apply(resource.getName())方法,进入到FlowSlot.apply()
public Collection<FlowRule> apply(String resource) {// 缓存规则Map<String, List<FlowRule>> flowRules = FlowRuleManager.getFlowRuleMap();return flowRules.get(resource);
}// 我们知道了是通过FlowRuleManager.getFlowRuleMap();这个流控规则管理器得到了流控规则集合。继续更近该方法
public class FlowRuleManager {// 找到了真正保存内存中规则对象的mapprivate static volatile Map<String, List<FlowRule>> flowRules = new HashMap<>();。。。
}



我们现在就知道了下面这些数据:

  • 微服务端 规则实体对象:FlowRule

  • 微服务端 缓存流控规则管理器:FlowRuleManager.getFlowRuleMap()

  • 微服务端 保存规则的集合定义Map<String, List<FlowRule>> flowRules = new HashMap<>()

  • 微服务端 加载规则进内存FlowRuleManager.loadRules(rules);



规则如何加载进内存

sentinel的入门代码如下

public static void main(String[] args) {// 配置规则.initFlowRules();while (true) {// 1.5.0 版本开始可以直接利用 try-with-resources 特性try (Entry entry = SphU.entry("HelloWorld")) {// 被保护的逻辑System.out.println("hello world");} catch (BlockException ex) {// 处理被流控的逻辑System.out.println("blocked!");}}
}private static void initFlowRules(){List<FlowRule> rules = new ArrayList<>();FlowRule rule = new FlowRule();rule.setResource("HelloWorld");rule.setGrade(RuleConstant.FLOW_GRADE_QPS);// Set limit QPS to 20.rule.setCount(20);rules.add(rule);// 核心代码,加载规则进内存// 可以发现 这里也是使用的流控规则管理器FlowRuleManagerFlowRuleManager.loadRules(rules);
}



真正将规则加载进内存中FlowRuleManager.loadRules(rules);

  • 遍历PropertyListener监听器集合,找对应的监听器去处理
  • 将传过来的list转换为我们对应的Map集合 Map<String, List<FlowRule>> rules
  • 直接复制给flowRules这个成员属性,这里就和上面微服务端规则如何保存串联起来了
public static void loadRules(List<FlowRule> rules) {// 加载资源进内存 Map<String, List<FlowRule>> flowRules 集合中currentProperty.updateValue(rules);
}// 调用进DynamicSentinelProperty#updateValue方法中
public boolean updateValue(T newValue) {if (isEqual(value, newValue)) {return false;}RecordLog.info("[DynamicSentinelProperty] Config will be updated to: {}", newValue);value = newValue;for (PropertyListener<T> listener : listeners) {// 对应的监听器去处理listener.configUpdate(newValue);}return true;
}// 这里会调用进FlowRuleManager的内部类FlowPropertyListener.configUpdate()
public synchronized void configUpdate(List<FlowRule> value) {// 将传过来的list转换为我们对应的Map集合Map<String, List<FlowRule>> rules = FlowRuleUtil.buildFlowRuleMap(value);if (rules != null) {// 直接复制给flowRules这个成员属性//  这里就和上面微服务端规则如何保存串联起来了flowRules = rules;}RecordLog.info("[FlowRuleManager] Flow rules received: {}", rules);
}



微服务端接收控制台请求

  • 我们知道我们使用sentinel和springcloudAlibaba整合会调用spring-cloud-starter-alibaba-sentinel依赖,而在这个依赖中的spring.factories文件中会定义一个SentinelWebAutoConfiguration自动配置类,它实现了WebMvcConfigurer接口,并重写addInterceptors()方法它会添加拦截器,拦截器中会对我们的请求进行拦截并添加定义资源的代码 Entry entry = SphU.entry(...)

  • 我们直接使用@SentinelResource注解方式,spring.factories文件中会定义一个SentinelAutoConfiguration自动配置类,并添加了一个SentinelResourceAspectBean对象,它通过AOP对要执行的目标方法也加了定义资源的代码 Entry entry = SphU.entry(...)



而在Env类的静态代码块中,会使用SPI加载InitFunc接口,加载出来的其中一个核心类,客户端启动的接口服务,提供给dashboard查询数据以及接收各种规则使用:com.alibaba.csp.sentinel.transport.init.CommandCenterInitFunc

  • 使用spi技术,加载各个CommandHandler,其中有ModifyRulesCommandHandler是处理修改规则的handler
  • 开启ServerSocket通信
public class CommandCenterInitFunc implements InitFunc {@Overridepublic void init() throws Exception {CommandCenter commandCenter = CommandCenterProvider.getCommandCenter();if (commandCenter == null) {RecordLog.warn("[CommandCenterInitFunc] Cannot resolve CommandCenter");return;}// 使用spi技术,加载各个CommandHandler,其中有ModifyRulesCommandHandler是处理修改规则的handlercommandCenter.beforeStart();// 开启ServerSocket通信commandCenter.start();RecordLog.info(...);}
}



进入ModifyRulesCommandHandler类的handler()方法:

  • 把控制台传递过来的请求参数转换为 List<FlowRule>
  • 加载规则,直接调用FlowRuleManager流控规则管理器的loadRules()方法
  • 写数据源的操作,默认情况下是没有WritableDataSource,我们可以在这里进行扩展进行持久化操作
  • 响应给sentinel控制台
// 注意name = "setRules",这就是控制台请求服务端的url路径
@CommandMapping(name = "setRules", desc = "modify the rules, accept param: type={ruleType}&data={ruleJson}")
public class ModifyRulesCommandHandler implements CommandHandler<String> {public CommandResponse<String> handle(CommandRequest request) {//......// 处理流控规则if (FLOW_RULE_TYPE.equalsIgnoreCase(type)) {// 把控制台传递过来的请求参数转换为 List<FlowRule>List<FlowRule> flowRules = JSONArray.parseArray(data, FlowRule.class);// 加载规则,直接调用FlowRuleManager流控规则管理器的loadRules()方法// 这里就和上方 微服务端规则是如何加载进内存的串联起来了FlowRuleManager.loadRules(flowRules);// 关键一步,这里会有一个写数据源的操作。默认情况下是没有WritableDataSource,我们可以在这里进行扩展if (!writeToDataSource(getFlowDataSource(), flowRules)) {result = WRITE_DS_FAILURE_MSG;}// 响应给sentinel控制台return CommandResponse.ofSuccess(result);// 处理权限规则} else if (AUTHORITY_RULE_TYPE.equalsIgnoreCase(type)) {List<AuthorityRule> rules = JSONArray.parseArray(data, AuthorityRule.class);AuthorityRuleManager.loadRules(rules);if (!writeToDataSource(getAuthorityDataSource(), rules)) {result = WRITE_DS_FAILURE_MSG;}return CommandResponse.ofSuccess(result);// 处理熔断规则} else if (DEGRADE_RULE_TYPE.equalsIgnoreCase(type)) {List<DegradeRule> rules = JSONArray.parseArray(data, DegradeRule.class);DegradeRuleManager.loadRules(rules);if (!writeToDataSource(getDegradeDataSource(), rules)) {result = WRITE_DS_FAILURE_MSG;}return CommandResponse.ofSuccess(result);// 处理系统规则} else if (SYSTEM_RULE_TYPE.equalsIgnoreCase(type)) {List<SystemRule> rules = JSONArray.parseArray(data, SystemRule.class);SystemRuleManager.loadRules(rules);if (!writeToDataSource(getSystemSource(), rules)) {result = WRITE_DS_FAILURE_MSG;}return CommandResponse.ofSuccess(result);}return CommandResponse.ofFailure(new IllegalArgumentException("invalid type"));}
}



控制台推送规则

我们在sentinel控制台进行了资源规则的更新,控制台是如何通知微服务进行相应的更新操作的嘞?

dashboard控制台处理web界面请求的位置是FlowControllerV1,请求路径为/v1/flow/rule,新增规则是POST请求,修改规则是PUT规则

  • 在dashboard这边也会将规则在内存中保存一份
  • 调用微服务端,更新规则
// 不管是新增规则还是更新规则,处理细枝末节的代码,关键代码就是下面这两行
public Result<FlowRuleEntity> apiAddFlowRule(@RequestBody FlowRuleEntity entity) {......try {// 在dashboard这边也会将规则在内存中进行保存entity = repository.save(entity);// 调用微服务端,更新规则publishRules(entity.getApp(), entity.getIp(), entity.getPort()).get(5000, TimeUnit.MILLISECONDS);return Result.ofSuccess(entity);} catch (Throwable t) {......}
}



首先看dashboard这边是如何将规则在内存中保存的,这里会调用到InMemoryRuleRepositoryAdapter#save,从这个类名就能看出来是操作内存的

这个类实现了RuleRepository接口,那么这里也是一个规则持久化的扩展点,我们可以自定义类,实现该接口,然后写相应的处理逻辑,在改一下dashboard控制台这边调用的源代码。(这种方式可以先考虑,但不是必须要这么做)

  • 将规则数据分别保存在allRules machineRules appRules这三个集合中
public T save(T entity) {if (entity.getId() == null) {entity.setId(nextId());}T processedEntity = preProcess(entity);if (processedEntity != null) {// 所有规则集合中保存一份allRules.put(processedEntity.getId(), processedEntity);// 对应微服务ip+port机器的集合中保存一份machineRules.computeIfAbsent(MachineInfo.of(processedEntity.getApp(), processedEntity.getIp(),processedEntity.getPort()), e -> new ConcurrentHashMap<>(32)).put(processedEntity.getId(), processedEntity);// appRules集合中也保存一份appRules.computeIfAbsent(processedEntity.getApp(), v -> new ConcurrentHashMap<>(32)).put(processedEntity.getId(), processedEntity);}return processedEntity;
}



在看看publishRules()是如何调用微服务端进行规则更新的

  • 从machineRules 对应微服务ip+port机器的集合 中把规则取出来List<FlowRuleEntity> rules
  • 发送请求给微服务端 http://ip:8719/setRules
private CompletableFuture<Void> publishRules(String app, String ip, Integer port) {// 通过machineRules  对应微服务ip+port机器的集合 中把规则取出来// 控制台的规则实体对象是FlowRuleEntity,而微服务端的规则实体对象是FlowRuleList<FlowRuleEntity> rules = repository.findAllByMachine(MachineInfo.of(app, ip, port));// 发送请求给微服务端return sentinelApiClient.setFlowRuleOfMachineAsync(app, ip, port, rules);
}// 方法调用
public CompletableFuture<Void> setFlowRuleOfMachineAsync(String app, String ip, int port, List<FlowRuleEntity> rules) {return setRulesAsync(app, ip, port, FLOW_RULE_TYPE, rules);}private CompletableFuture<Void> setRulesAsync(String app, String ip, int port, String type, List<? extends RuleEntity> entities) {try {AssertUtil.notNull(entities, "rules cannot be null");AssertUtil.notEmpty(app, "Bad app name");AssertUtil.notEmpty(ip, "Bad machine IP");AssertUtil.isTrue(port > 0, "Bad machine port");// 请求参数data// toRule()方法中会把控制台的规则实体对象转换为微服务端的规则实体对象// 以流控为例 控制台的规则实体对象是FlowRuleEntity,而微服务端的规则实体对象是FlowRuleString data = JSON.toJSONString(entities.stream().map(r -> r.toRule()).collect(Collectors.toList()));Map<String, String> params = new HashMap<>(2);params.put("type", type);params.put("data", data);// 想微服务端发送请求  SET_RULES_PATH = "setRules"   // 所以调用微服务的url是 http://ip:8719/setRules  ,微服务端启动时8719端口如果被占用则会往后自增,继续找端口// 这里就和上面的服务端接收控制台请求串联起来了return executeCommand(app, ip, port, SET_RULES_PATH, params, true).thenCompose(r -> {if ("success".equalsIgnoreCase(r.trim())) {return CompletableFuture.completedFuture(null);}return AsyncUtils.newFailedFuture(new CommandFailedException(r));});} catch (Exception e) {logger.error("setRulesAsync API failed, type={}", type, e);return AsyncUtils.newFailedFuture(e);}}



总结

微服务端

  • 微服务端 规则实体对象:FlowRule

  • 微服务端 缓存流控规则管理器:FlowRuleManager.getFlowRuleMap()

  • 微服务端 保存规则的集合定义Map<String, List<FlowRule>> flowRules = new HashMap<>()

  • 微服务端 加载规则进内存FlowRuleManager.loadRules(rules);



控制台端

处理请求 FlowControllerV1 /v1/flow/rule

规则实体: FlowRuleEntity

缓存规则: 基于内存模式 allRules machineRules appRules

接口: RuleRepository 规则持久化的扩展点(考虑)

发布缓存: 推送到微服务端内存中

  • 请求参数转换: FlowRuleEntity ——》FlowRule
  • 发起请求: http://ip:8719/setRules



微服务端接收请求:

ModifyRulesCommandHandler#handle

加载规则: FlowRuleManager.loadRules(flowRules)

接口: WritableDataSource 规则持久化的扩展点



pull拉模式

pull 模式的数据源(如本地文件、RDBMS 等)一般是可写入的。使用时需要在客户端注册数据源:

  • 将对应的读数据源注册至对应的 RuleManager
  • 将写数据源注册至 transport 的 WritableDataSourceRegistry 中。

在这里插入图片描述



首先 Sentinel 控制台通过 API 将规则推送至客户端并更新到内存中,接着注册的写数据源会将新的规则保存到本地的文件中。

当我们直接修改本地文件,读数据源的线程会定期3s读取文件,将变更的配置更新到内存中

使用 pull 模式的数据源时一般不需 要对 Sentinel 控制台进行改造。这种实现方法好处是简单,坏处是无法保证监控数据的一致性。



官方demo

在sentinel的源码中,它其实提供了一些写文件的demo,位置在下图的位置

在这里插入图片描述



具体实现的核心的代码就是下面这个类

import java.io.File;
import java.util.List;import com.alibaba.csp.sentinel.datasource.FileRefreshableDataSource;
import com.alibaba.csp.sentinel.datasource.FileWritableDataSource;
import com.alibaba.csp.sentinel.datasource.ReadableDataSource;
import com.alibaba.csp.sentinel.datasource.WritableDataSource;
import com.alibaba.csp.sentinel.init.InitFunc;
import com.alibaba.csp.sentinel.slots.block.flow.FlowRule;
import com.alibaba.csp.sentinel.slots.block.flow.FlowRuleManager;
import com.alibaba.csp.sentinel.transport.util.WritableDataSourceRegistry;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;public class FileDataSourceInit implements InitFunc {@Overridepublic void init() throws Exception {//定义文件路径 String flowRuleDir = System.getProperty("user.home") + File.separator + "sentinel" + File.separator + "rules";String flowRuleFile = "flowRule.json";String flowRulePath = flowRuleDir + File.separator + flowRuleFile;// 将对应的读数据源注册至对应的FlowRuleManagerReadableDataSource<String, List<FlowRule>> ds = new FileRefreshableDataSource<>(flowRulePath, source -> JSON.parseObject(source, new TypeReference<List<FlowRule>>() {}));FlowRuleManager.register2Property(ds.getProperty());// 将写数据源注册至 transport 的WritableDataSourceRegistry中WritableDataSource<List<FlowRule>> wds = new FileWritableDataSource<>(flowRulePath, this::encodeJson);WritableDataSourceRegistry.registerFlowDataSource(wds);}private <T> String encodeJson(T t) {return JSON.toJSONString(t);}
}



我们先看看将对应的读数据源注册至对应的FlowRuleManager这一块的逻辑,读数据源FileRefreshableDataSource它的结构如下

在这里插入图片描述


关键代码

// FileRefreshableDataSource类方法     定义文件输入流   校验文件是否更新
public String readSource() throws Exception {// ......// 创建文件输入流,文件的最后修改时间进行判断,文件修改后父类就会调用到这里来FileInputStream inputStream = null;try {inputStream = new FileInputStream(file);FileChannel channel = inputStream.getChannel();if (channel.size() > buf.length) {throw new IllegalStateException(file.getAbsolutePath() + " file size=" + channel.size()+ ", is bigger than bufSize=" + buf.length + ". Can't read");}int len = inputStream.read(buf);return new String(buf, 0, len, charset);} finally {// ......}
}// 根据文件最后的修改时间进行判断
protected boolean isModified() {long curLastModified = file.lastModified();if (curLastModified != this.lastModified) {this.lastModified = curLastModified;return true;}return false;
}//---------------------------------------------------------------------------------------------
// AutoRefreshDataSource 中间父类方法 。 开启线程任务 调用父类方法 加载配置 +  更新配置
private void startTimerService() {// 创建只有一个线程的线程池service = Executors.newScheduledThreadPool(1,new NamedThreadFactory("sentinel-datasource-auto-refresh-task", true));// 每隔3s执行一次定时任务service.scheduleAtFixedRate(new Runnable() {@Overridepublic void run() {try {// 调用子类方法  获取文件的最后修改时间进行判断if (!isModified()) {return;}// 调用父类方法 加载配置T newValue = loadConfig();// 更新配置getProperty().updateValue(newValue);} catch (Throwable e) {RecordLog.info("loadConfig exception", e);}}}, recommendRefreshMs, recommendRefreshMs, TimeUnit.MILLISECONDS);
}//---------------------------------------------------------------------------------------------
// AbstractDataSource定级父类方法。   调用子类的readSource()方法,并解析配置
public T loadConfig() throws Exception {return loadConfig(readSource());
}public T loadConfig(S conf) throws Exception {// 解析配置T value = parser.convert(conf);return value;
}



我们在看看将写数据源注册至 transport 的WritableDataSourceRegistry中。点进去就会发现这里会和我们微服务端接收控制台请求时处理请求串联起来

我们这边保存写数据源,而微服务端每次接收到控制台的规则更改请求后,都会获取写数据源进行相应的写入操作。

在这里插入图片描述



所以最核心的代码就是下面这一块

public void init() throws Exception {//定义文件路径 String flowRuleDir = System.getProperty("user.home") + File.separator + "sentinel" + File.separator + "rules";String flowRuleFile = "flowRule.json";String flowRulePath = flowRuleDir + File.separator + flowRuleFile;// 将对应的读数据源注册至对应的FlowRuleManagerReadableDataSource<String, List<FlowRule>> ds = new FileRefreshableDataSource<>(flowRulePath, source -> JSON.parseObject(source, new TypeReference<List<FlowRule>>() {}));FlowRuleManager.register2Property(ds.getProperty());// 将写数据源注册至 transport 的WritableDataSourceRegistry中WritableDataSource<List<FlowRule>> wds = new FileWritableDataSource<>(flowRulePath, this::encodeJson);WritableDataSourceRegistry.registerFlowDataSource(wds);
}

这一块的代码如果要和我们SpringCloud微服务进行整合,那应该如何做嘞?



如何整合Spring Cloud

扩展点:

  • spi

spring:

  • beanPostProcessor beanFactoryPostProcessor

  • SmartInitializingSingleton

  • ApplicationListener

  • FactoryBean getObject

springboot

  • ApplicationRunner



整合Spring Cloud

我这里使用SPI的方式进行。因为Sentinel本来就有SPI的机制

我们直接在sentinel源码下创建一个子工程,并在META-INF/services目录下创建InitFunc接口对应的文件

请添加图片描述



引入的maven依赖如下

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><parent><artifactId>sentinel-extension</artifactId><groupId>com.alibaba.csp</groupId><version>1.8.4</version></parent><modelVersion>4.0.0</modelVersion><artifactId>sentinel-datasource-extension-file-pull</artifactId><packaging>jar</packaging><dependencies><dependency><groupId>com.alibaba.csp</groupId><artifactId>sentinel-core</artifactId></dependency><dependency><groupId>com.alibaba.csp</groupId><artifactId>sentinel-datasource-extension</artifactId></dependency><dependency><groupId>com.alibaba.csp</groupId><artifactId>sentinel-transport-simple-http</artifactId></dependency><dependency><groupId>com.alibaba</groupId><artifactId>fastjson</artifactId></dependency><dependency><groupId>com.alibaba.csp</groupId><artifactId>sentinel-parameter-flow-control</artifactId></dependency></dependencies><build><plugins><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-source-plugin</artifactId><version>3.0.1</version><executions><execution><id>attach-sources</id><goals><goal>jar</goal></goals></execution></executions></plugin></plugins></build></project>



然后编写代码如下,其实就是官方demo的那几行代码的具体实现。

/** Copyright 1999-2018 Alibaba Group Holding Ltd.** Licensed under the Apache License, Version 2.0 (the "License");* you may not use this file except in compliance with the License.* You may obtain a copy of the License at**      http://www.apache.org/licenses/LICENSE-2.0** Unless required by applicable law or agreed to in writing, software* distributed under the License is distributed on an "AS IS" BASIS,* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.* See the License for the specific language governing permissions and* limitations under the License.*/
package com.tuling.sentinel.extension.filepull;import com.alibaba.csp.sentinel.command.handler.ModifyParamFlowRulesCommandHandler;
import com.alibaba.csp.sentinel.datasource.FileRefreshableDataSource;
import com.alibaba.csp.sentinel.datasource.FileWritableDataSource;
import com.alibaba.csp.sentinel.datasource.ReadableDataSource;
import com.alibaba.csp.sentinel.datasource.WritableDataSource;
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 java.io.FileNotFoundException;
import java.util.List;/*** InitFunc实现类,处理dataSource初始化逻辑**/
public class FileDataSourceInit implements InitFunc {@Overridepublic void init() throws Exception {//创建文件存储目录RuleFileUtils.mkdirIfNotExits(PersistenceRuleConstant.storePath);//创建规则文件RuleFileUtils.createFileIfNotExits(PersistenceRuleConstant.rulesMap);//处理流控规则逻辑  配置读写数据源dealFlowRules();// 处理降级规则dealDegradeRules();// 处理系统规则dealSystemRules();// 处理热点参数规则dealParamFlowRules();// 处理授权规则dealAuthRules();}private void dealFlowRules() throws FileNotFoundException {String ruleFilePath = PersistenceRuleConstant.rulesMap.get(PersistenceRuleConstant.FLOW_RULE_PATH).toString();//创建流控规则的可读数据源ReadableDataSource<String, List<FlowRule>> flowRuleRDS = new FileRefreshableDataSource(ruleFilePath, RuleListConverterUtils.flowRuleListParser);// 将可读数据源注册至FlowRuleManager 这样当规则文件发生变化时,就会更新规则到内存FlowRuleManager.register2Property(flowRuleRDS.getProperty());WritableDataSource<List<FlowRule>> flowRuleWDS = new FileWritableDataSource<List<FlowRule>>(ruleFilePath, RuleListConverterUtils.flowFuleEnCoding);// 将可写数据源注册至 transport 模块的 WritableDataSourceRegistry 中.// 这样收到控制台推送的规则时,Sentinel 会先更新到内存,然后将规则写入到文件中.WritableDataSourceRegistry.registerFlowDataSource(flowRuleWDS);}private void dealDegradeRules() throws FileNotFoundException {//获取规则文件路径String degradeRuleFilePath = PersistenceRuleConstant.rulesMap.get(PersistenceRuleConstant.DEGRAGE_RULE_PATH).toString();//创建流控规则的可读数据源ReadableDataSource<String, List<DegradeRule>> degradeRuleRDS = new FileRefreshableDataSource(degradeRuleFilePath, RuleListConverterUtils.degradeRuleListParse);// 将可读数据源注册至FlowRuleManager 这样当规则文件发生变化时,就会更新规则到内存DegradeRuleManager.register2Property(degradeRuleRDS.getProperty());WritableDataSource<List<DegradeRule>> degradeRuleWDS = new FileWritableDataSource<>(degradeRuleFilePath, RuleListConverterUtils.degradeRuleEnCoding);// 将可写数据源注册至 transport 模块的 WritableDataSourceRegistry 中.// 这样收到控制台推送的规则时,Sentinel 会先更新到内存,然后将规则写入到文件中.WritableDataSourceRegistry.registerDegradeDataSource(degradeRuleWDS);}private void dealSystemRules() throws FileNotFoundException {//获取规则文件路径String systemRuleFilePath = PersistenceRuleConstant.rulesMap.get(PersistenceRuleConstant.SYSTEM_RULE_PATH).toString();//创建流控规则的可读数据源ReadableDataSource<String, List<SystemRule>> systemRuleRDS = new FileRefreshableDataSource(systemRuleFilePath, RuleListConverterUtils.sysRuleListParse);// 将可读数据源注册至FlowRuleManager 这样当规则文件发生变化时,就会更新规则到内存SystemRuleManager.register2Property(systemRuleRDS.getProperty());WritableDataSource<List<SystemRule>> systemRuleWDS = new FileWritableDataSource<>(systemRuleFilePath, RuleListConverterUtils.sysRuleEnCoding);// 将可写数据源注册至 transport 模块的 WritableDataSourceRegistry 中.// 这样收到控制台推送的规则时,Sentinel 会先更新到内存,然后将规则写入到文件中.WritableDataSourceRegistry.registerSystemDataSource(systemRuleWDS);}private void dealParamFlowRules() throws FileNotFoundException {//获取规则文件路径String paramFlowRuleFilePath = PersistenceRuleConstant.rulesMap.get(PersistenceRuleConstant.HOT_PARAM_RULE).toString();//创建流控规则的可读数据源ReadableDataSource<String, List<ParamFlowRule>> paramFlowRuleRDS = new FileRefreshableDataSource(paramFlowRuleFilePath, RuleListConverterUtils.paramFlowRuleListParse);// 将可读数据源注册至FlowRuleManager 这样当规则文件发生变化时,就会更新规则到内存ParamFlowRuleManager.register2Property(paramFlowRuleRDS.getProperty());WritableDataSource<List<ParamFlowRule>> paramFlowRuleWDS = new FileWritableDataSource<>(paramFlowRuleFilePath, RuleListConverterUtils.paramRuleEnCoding);// 将可写数据源注册至 transport 模块的 WritableDataSourceRegistry 中.// 这样收到控制台推送的规则时,Sentinel 会先更新到内存,然后将规则写入到文件中.ModifyParamFlowRulesCommandHandler.setWritableDataSource(paramFlowRuleWDS);}private void dealAuthRules() throws FileNotFoundException {//获取规则文件路径String authFilePath = PersistenceRuleConstant.rulesMap.get(PersistenceRuleConstant.AUTH_RULE_PATH).toString();//创建流控规则的可读数据源ReadableDataSource<String, List<AuthorityRule>> authRuleRDS = new FileRefreshableDataSource(authFilePath, RuleListConverterUtils.authorityRuleParse);// 将可读数据源注册至FlowRuleManager 这样当规则文件发生变化时,就会更新规则到内存AuthorityRuleManager.register2Property(authRuleRDS.getProperty());//创建流控规则的写数据源WritableDataSource<List<AuthorityRule>> authRuleWDS = new FileWritableDataSource<>(authFilePath, RuleListConverterUtils.authorityEncoding);// 将可写数据源注册至 transport 模块的 WritableDataSourceRegistry 中.// 这样收到控制台推送的规则时,Sentinel 会先更新到内存,然后将规则写入到文件中.WritableDataSourceRegistry.registerAuthorityDataSource(authRuleWDS);}}
package com.tuling.sentinel.extension.filepull;import java.io.File;
import java.io.IOException;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;/*** 创建规则持久化目录和文件的工具类**/
public class RuleFileUtils {public static void mkdirIfNotExits(String filePath) throws IOException {File file = new File(filePath);if(!file.exists()) {file.mkdirs();}}public static void createFileIfNotExits(Map<String,String> ruleFileMap) throws IOException {Set<String> ruleFilePathSet = ruleFileMap.keySet();Iterator<String> ruleFilePathIter = ruleFilePathSet.iterator();while (ruleFilePathIter.hasNext()) {String ruleFilePathKey = ruleFilePathIter.next();String ruleFilePath  = PersistenceRuleConstant.rulesMap.get(ruleFilePathKey).toString();File ruleFile = new File(ruleFilePath);if(!ruleFile.exists()) {ruleFile.createNewFile();}}}}
package com.tuling.sentinel.extension.filepull;import java.io.File;
import java.util.HashMap;
import java.util.Map;/*** Sentinel 规则持久化 常量配置类**/
public class PersistenceRuleConstant {/*** 存储文件路径*/public static final String storePath = System.getProperty("user.home") + File.separator + "sentinel" + File.separator + "rules";/*** 各种存储sentinel规则映射map*/public static final Map rulesMap = new HashMap<String,String>();//流控规则文件public static final String FLOW_RULE_PATH = "flowRulePath";//降级规则文件public static final String DEGRAGE_RULE_PATH = "degradeRulePath";//授权规则文件public static final String AUTH_RULE_PATH = "authRulePath";//系统规则文件public static final String SYSTEM_RULE_PATH = "systemRulePath";//热点参数文件public static final String HOT_PARAM_RULE = "hotParamRulePath";static {rulesMap.put(FLOW_RULE_PATH,storePath+ File.separator +"flowRule.json");rulesMap.put(DEGRAGE_RULE_PATH,storePath+File.separator +"degradeRule.json");rulesMap.put(SYSTEM_RULE_PATH,storePath+File.separator +"systemRule.json");rulesMap.put(AUTH_RULE_PATH,storePath+File.separator +"authRule.json");rulesMap.put(HOT_PARAM_RULE,storePath+File.separator +"hotParamRule.json");}
}
package com.tuling.sentinel.extension.filepull;import com.alibaba.csp.sentinel.datasource.Converter;
import com.alibaba.csp.sentinel.slots.block.authority.AuthorityRule;
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRule;
import com.alibaba.csp.sentinel.slots.block.flow.FlowRule;
import com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowRule;
import com.alibaba.csp.sentinel.slots.system.SystemRule;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;import java.util.List;/*** 规则列表解析工具类**/
public class RuleListConverterUtils {public static final Converter<String, List<FlowRule>> flowRuleListParser = new Converter<String, List<FlowRule>>() {@Overridepublic List<FlowRule> convert(String source) {return JSON.parseObject(source, new TypeReference<List<FlowRule>>() {});}};public static final Converter<String,List<DegradeRule>> degradeRuleListParse = new Converter<String, List<DegradeRule>>() {@Overridepublic List<DegradeRule> convert(String source) {return JSON.parseObject(source,new TypeReference<List<DegradeRule>>(){});}};public static final Converter<String,List<SystemRule>> sysRuleListParse = new Converter<String, List<SystemRule>>() {@Overridepublic List<SystemRule> convert(String source) {return JSON.parseObject(source,new TypeReference<List<SystemRule>>(){});}};public static final Converter<String,List<ParamFlowRule>> paramFlowRuleListParse = new Converter<String, List<ParamFlowRule>>() {@Overridepublic List<ParamFlowRule> convert(String source) {return JSON.parseObject(source,new TypeReference<List<ParamFlowRule>>(){});}};public static final Converter<String,List<AuthorityRule>> authorityRuleParse = new Converter<String, List<AuthorityRule>>() {@Overridepublic List<AuthorityRule> convert(String source) {return JSON.parseObject(source,new TypeReference<List<AuthorityRule>>(){});}};public static final Converter<List<FlowRule>,String> flowFuleEnCoding= new Converter<List<FlowRule>,String>() {@Overridepublic String convert(List<FlowRule> source) {return JSON.toJSONString(source);}};public static final Converter<List<SystemRule>,String> sysRuleEnCoding= new Converter<List<SystemRule>,String>() {@Overridepublic String convert(List<SystemRule> source) {return JSON.toJSONString(source);}};public static final Converter<List<DegradeRule>,String> degradeRuleEnCoding= new Converter<List<DegradeRule>,String>() {@Overridepublic String convert(List<DegradeRule> source) {return JSON.toJSONString(source);}};public static final Converter<List<ParamFlowRule>,String> paramRuleEnCoding= new Converter<List<ParamFlowRule>,String>() {@Overridepublic String convert(List<ParamFlowRule> source) {return JSON.toJSONString(source);}};public static final Converter<List<AuthorityRule>,String> authorityEncoding= new Converter<List<AuthorityRule>,String>() {@Overridepublic String convert(List<AuthorityRule> source) {return JSON.toJSONString(source);}};
}



打一个jar之后上传到公司仓库。

在微服务中引入下面的依赖即可

<dependency><groupId>com.alibaba.csp</groupId><artifactId>sentinel-datasource-extension-file-pull</artifactId><version>1.8.4</version>
</dependency>

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/376359.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

秋招Java后端开发冲刺——MyBatisPlus总结

一、 基本知识 1. 介绍 yBatis-Plus 是一个 MyBatis 的增强工具&#xff0c;在 MyBatis 的基础上增加了大量功能和简化操作&#xff0c;以提高开发效率。 2. 特点 无侵入&#xff1a;只做增强不做改变&#xff0c;引入它不会对现有项目产生影响。依赖少&#xff1a;仅仅依赖 …

谈谈软件交互设计

谈谈软件交互设计 交互设计的由来 交互设计(Interaction Design)这一概念,最初是由IDEO创始人之一Bill.Moggridge(莫格里奇)1984年在一次会议上提出。他设计了世界上第一台笔记本电脑Compass,并写作出版了在交互设计领域影响深远的《Designing Interactions》一书,被称…

mqtt.fx连接阿里云

本文主要是记述一下如何使用mqtt.fx连接在阿里云上创建好的MQTT服务。 1 根据MQTT填写对应端口即可 找到设备信息&#xff0c;里面有MQTT连接参数 2 使用物模型通信Topic&#xff0c;注意这里的post说设备上报&#xff0c;那也就是意味着云端订阅post&#xff1b;set则意味着设…

Linux内核编译安装 - Deepin,Debian系

为什么要自己编译内核 优点 定制化&#xff1a;你可以根据自己的硬件和需求配置内核&#xff0c;去掉不必要的模块&#xff0c;优化性能。性能优化&#xff1a;移除不需要的驱动程序和特性&#xff0c;减小内核体积&#xff0c;提高系统性能。最新特性和修复&#xff1a;获取…

【密码学】从有限状态自动机到密钥流生成器

本文是对流密码内容的拓展&#xff0c;在流密码中种子密钥通过一个伪随机数生成器产生一个与明文等长的伪随机密钥流。而本文的内容就是在回答这样两个问题&#xff1a; 伪随机密钥流是如何生成的&#xff1f;流密码、流密钥生成器和有限状态自动机之间是什么关系&#xff1f;…

Mac和VirtualBox Ubuntu共享文件夹

1、VirtualBox中点击设置->共享文件夹 2、设置共享文件夹路径和名称&#xff08;重点来了&#xff1a;共享文件夹名称&#xff09; 3、保存设置后重启虚拟机&#xff0c;执行下面的命令 sudo mkdir /mnt/share sudo mount -t vboxsf share /mnt/share/ 注&#xff1a;shar…

Gitea 仓库事件触发Jenkins远程构建

文章目录 引言I Gitea 仓库事件触发Jenkins远程构建1.1 Jenkins配置1.2 Gitea 配置引言 应用场景:项目部署 I Gitea 仓库事件触发Jenkins远程构建 Gitea支持用于仓库事件的Webhooks 1.1 Jenkins配置 高版本Jenkins需要关闭跨域限制和开启匿名用户访问 在Jenkins启动前加入…

东软“引战”国家队 通用技术“补链”大国重器

向来低调温和的东软创始人刘积仁&#xff0c;这一次抛出了“王炸”级的资产交易。 7月3日&#xff0c;《多肽链》获得一则足以引爆国内医疗设备行业的投资信息&#xff1a;被东软集团视为核心资产、掌上明珠的东软医疗&#xff0c;成功引入通用技术集团资本有限公司与中国国有…

华为配置蓝牙终端定位实验

个人主页&#xff1a;知孤云出岫 目录 配置蓝牙终端定位示例 业务需求 组网需求 数据规划 配置思路 配置注意事项 操作步骤 配置文件 配置蓝牙终端定位示例 组网图形 图1 配置蓝牙终端定位示例组网图 业务需求组网需求数据规划配置思路配置注意事项操作步骤配置文件 业…

用HTML和CSS实现提示工具(tooltip)及HTML元素的定位

所谓提示工具&#xff0c;是指将鼠标移动到某个HTML元素&#xff08;工具&#xff09;时会显示一些提示内容&#xff08;提示文本&#xff09;&#xff0c;而鼠标移出工具元素的范围时提示文本就消失了。考虑到提示文本元素应当在鼠标进入工具元素时显示&#xff0c;鼠标离开工…

【VS2019】安装下载库HtmlAgilityPack,可解析 HTML (图文详情)

目录 0.背景 1.环境 2.详细步骤 0.背景 项目需要&#xff0c;搭建WCF服务&#xff0c;需求是输入一个string类型字符串&#xff08;网页代码&#xff0c;如<html><body><p>Hello, <b>World</b>!</p></body></html>&#xf…

《代理选择与反爬虫策略探究:如何优化网络爬虫效率与稳定性》

代理IP如何选以及常见反爬策略 为什么需要代理&#xff1f; 因为有的网站会封IP&#xff0c;用户如果没有登录&#xff0c;那IP就是身份标识&#xff0c;如果网站发现用户行为异常就非常可能封IP 什么是代理IP 就是让一个人帮你转交请求&#xff0c;帮你转交的人对面不熟&a…

<数据集>猫狗识别数据集<目标检测>

数据集格式&#xff1a;VOCYOLO格式 图片数量&#xff1a;3686张 标注数量(xml文件个数)&#xff1a;3686 标注数量(txt文件个数)&#xff1a;3686 标注类别数&#xff1a;2 标注类别名称&#xff1a;[cat, dog] 序号类别名称图片数框数1cat118811892dog24982498 使用标…

vmware 虚拟机扩容 centos 硬盘扩容 kylinos v10扩容

1. 虚拟机先扩容 1.1 关机&#xff0c;并点击系统&#xff0c;让他是点选状态&#xff0c;但是没开机 1.2 右击&#xff0c;点击最下方设置&#xff0c;点击硬盘 1.3 点击扩展磁盘 1.4 选择你需要扩容的大小&#xff0c;数字为总大小 完成提示&#xff1a; 磁盘已成功扩展。您…

整洁架构SOLID-接口隔离原则(ISP)

文章目录 定义ISP与编程语言ISP与软件架构小结 定义 在上图中有多个用户需要操作OPS类。现在&#xff0c;我们假设这里的User1只需要使用op1,User2只需要使用op2,User3只需要使用op3。 在这种情况下&#xff0c;如果OPS类是用Java编程语言编写的&#xff0c;那么很明显&#x…

安全防御实验2

一、实验拓扑 二、实验要求 办公区设备可以通过电信链路和移动链路上网(多对多的NAT&#xff0c;并且需要保留一个公网IP不能用来转换)分公司设备可以通过总公司的移动链路和电信链路访问到Dmz区的http服务器多出口环境基于带宽比例进行选路&#xff0c;但是&#xff0c;办公区…

electron + express 实现 vue 项目客户端部署

写在前面 作为一个前端程序员&#xff0c;如何实现从前端到客户端的跨越&#xff0c;可能是一个很难实现的事。但客户需求千奇百怪&#xff0c;偶尔遇到一个非要客户端的&#xff0c;如何应对&#xff1f; 那Electron可能真是你福音。具体它有哪些功能&#xff0c;可自行官网…

《斯科特·凯尔比的风光摄影手册》读书笔记

写在前面 《斯科特凯尔比的风光摄影手册》读书笔记整理没有全部读完&#xff0c;选择了感兴趣的章节理解不足小伙伴帮忙指正 &#x1f603;,生活加油 99%的焦虑都来自于虚度时间和没有好好做事&#xff0c;所以唯一的解决办法就是行动起来&#xff0c;认真做完事情&#xff0c;…

2-33 基于matlab的用于计算无故障的斜齿轮对啮合时接触线长度随时间的变化

基于matlab的用于计算无故障的斜齿轮对啮合时接触线长度随时间的变化&#xff0c;根据需求设置斜齿轮对的相应参数&#xff0c;得到结果。程序已调通&#xff0c;可直接运行。 2-33 斜齿轮对啮合时接触线长度 齿轮参数 - 小红书 (xiaohongshu.com)

MongoDB - 查询操作符:比较查询、逻辑查询、元素查询、数组查询

文章目录 1. 构造数据2. MongoDB 比较查询操作符1. $eq 等于1.1 等于指定值1.2 嵌入式文档中的字段等于某个值1.3 数组元素等于某个值1.4 数组元素等于数组值 2. $ne 不等于3. $gt 大于3.1 匹配文档字段3.2 根据嵌入式文档字段执行更新 4. $gte 大于等于5. $lt 小于6. $lte 小于…