Sentinel规则长期化Push模式两种实现方式

[复制链接]
发表于 2026-2-22 11:03:41 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?立即注册

×
前置知识 pull模式

sentinel长期化push推模式

pull拉模式的缺点,以生存当地文件举例:

      
  • 定时使命是每隔3s实验一次,去判断规则长期化文件的末了修改时间。这里有肯定时间的延长,但如果时间设置的太短,有影响服务器的性能  
  • 我们的微服务是集群摆设的,其他服务实例可读取不到我这台服务器的当地文件


以是另有一种push推送模式。我们一样平常会引入第三方中心件来实现,以Nacos为例。我们修改了nacos中的设置,它就会将更新后的数据推送给微服务。


push模式有两种实现方式:

      
  • 在微服务端添加读数据源,为dataId添加监听器,当规则设置文件更改之后我就获取到更改后的规则内存并更新内存中的数据;再添加一个写数据源,每当dashboard中更新了规则,我除了更新内存中的数据之外,我通过ConfigService.publishConfig()方法还往Nacos端举行写入
      
  • 在dashboard源码中举行更改,在获取规则内容、更新规则内容的接口中,不要和微服务端举行交互,直接去和Nacos通讯,通过ConfigService.publishConfig()和ConfigService.getConfig()来实现。这种方式告急注意dashboard端的规则实体对象和微服务端的规则实体对象不划一题目,须要颠末转换干系的操纵。sentinel默认情况下就直接把规则实体转换为json字符串推送给Nacos,Nacos设置文件更改了,又推送给微服务,微服务这边再把json字符串转换为规则实体对象这一步就会发现,转换失败了,某些属性对应不上。进而就导致了dashboard端设置的规则在微服务这边未收效。



微服务端的实现

具体实现

引入读数据源的依靠
  1. <dependency>
  2.     <groupId>com.alibaba.csp</groupId>
  3.     <artifactId>sentinel-datasource-nacos</artifactId>
  4. </dependency>
复制代码


设置文件中添加规则长期化的dataId
  1. server:
  2.   port: 8806
  3. spring:
  4.   application:
  5.     name: mall-user-sentinel-rule-push  #微服务名称
  6.    
  7.   #配置nacos注册中心地址
  8.   cloud:
  9.     nacos:
  10.       discovery:
  11.         server-addr: 127.0.0.1:8848
  12.     sentinel:
  13.       transport:
  14.         # 添加sentinel的控制台地址
  15.         dashboard: 127.0.0.1:8080
  16.       datasource:
  17.         # 名称自定义,可以随便定义字符串
  18.         flow-rules:
  19.           nacos:
  20.             server-addr: 127.0.0.1:8848
  21.             # dataId取了微服务名字,后面再拼接字符串
  22.             dataId: ${
  23.    
  24.    spring.application.name}-flow-rules
  25.             # 我这里在Nacos配置中心,单独使用了一个组
  26.             groupId: SENTINEL_GROUP
  27.             username: nacos
  28.             password: nacos
  29.             data-type: json
  30.             rule-type: flow
  31.         degrade-rules:
  32.           nacos:
  33.             server-addr: 127.0.0.1:8848
  34.             dataId: ${
  35.    
  36.    spring.application.name}-degrade-rules
  37.             groupId: SENTINEL_GROUP
  38.             username: nacos
  39.             password: nacos
  40.             data-type: json
  41.             rule-type: degrade
  42.         param-flow-rules:
  43.           nacos:
  44.             server-addr: 127.0.0.1:8848
  45.             dataId: ${
  46.    
  47.    spring.application.name}-param-flow-rules
  48.             groupId: SENTINEL_GROUP
  49.             username: nacos
  50.             password: nacos
  51.             data-type: json
  52.             rule-type: param-flow
  53.         authority-rules:
  54.           nacos:
  55.             server-addr: 127.0.0.1:8848
  56.             dataId: ${
  57.    
  58.    spring.application.name}-authority-rules
  59.             groupId: SENTINEL_GROUP
  60.             username: nacos
  61.             password: nacos
  62.             data-type: json
  63.             rule-type: authority
  64.         system-rules:
  65.           nacos:
  66.             server-addr: 127.0.0.1:8848
  67.             dataId: ${
  68.    
  69.    spring.application.name}-system-rules
  70.             groupId: SENTINEL_GROUP
  71.             username: nacos
  72.             password: nacos
  73.             data-type: json
  74.             rule-type: system
复制代码


在Nacos设置中心中创建对应的设置文件



编写java类,界说写数据源
  1. import com.alibaba.cloud.sentinel.SentinelProperties;
  2. import com.alibaba.cloud.sentinel.custom.SentinelAutoConfiguration;
  3. import org.springframework.boot.autoconfigure.AutoConfigureAfter;
  4. import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
  5. import org.springframework.context.annotation.Bean;
  6. import org.springframework.context.annotation.Configuration;
  7. /**
  8. * 添加往Nacos的写数据源,只不过未使用InitFunc
  9. * 如果要使用就需要放开注解
  10. */
  11. @Configuration(proxyBeanMethods = false)
  12. @AutoConfigureAfter(SentinelAutoConfiguration.class)
  13. public class SentinelNacosDataSourceConfiguration {
  14.    
  15.    
  16.     @Bean
  17.     @ConditionalOnMissingBean
  18.     public SentinelNacosDataSourceHandler sentinelNacosDataSourceHandler(SentinelProperties sentinelProperties) {
  19.    
  20.    
  21.         return new SentinelNacosDataSourceHandler(sentinelProperties);
  22.     }
  23. }
复制代码
  1. import com.alibaba.cloud.sentinel.SentinelProperties;
  2. import com.alibaba.cloud.sentinel.datasource.RuleType;
  3. import com.alibaba.cloud.sentinel.datasource.config.DataSourcePropertiesConfiguration;
  4. import com.alibaba.cloud.sentinel.datasource.config.NacosDataSourceProperties;
  5. import com.alibaba.csp.sentinel.command.handler.ModifyParamFlowRulesCommandHandler;
  6. import com.alibaba.csp.sentinel.datasource.WritableDataSource;
  7. import com.alibaba.csp.sentinel.slots.block.authority.AuthorityRule;
  8. import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRule;
  9. import com.alibaba.csp.sentinel.slots.block.flow.FlowRule;
  10. import com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowRule;
  11. import com.alibaba.csp.sentinel.slots.system.SystemRule;
  12. import com.alibaba.csp.sentinel.transport.util.WritableDataSourceRegistry;
  13. import com.alibaba.fastjson.JSON;
  14. import org.springframework.beans.factory.SmartInitializingSingleton;
  15. import java.util.List;
  16. /**
  17. * sentinel 规则持久化到 nacos配置中心
  18. */
  19. public class SentinelNacosDataSourceHandler implements SmartInitializingSingleton {
  20.    
  21.    
  22.     private final SentinelProperties sentinelProperties;
  23.     public SentinelNacosDataSourceHandler(SentinelProperties sentinelProperties) {
  24.    
  25.    
  26.         this.sentinelProperties = sentinelProperties;
  27.     }
  28.     @Override
  29.     public void afterSingletonsInstantiated() {
  30.    
  31.    
  32.         // 遍历我们配置文件中指定的多个spring.cloud.sentinel.datasource的多个配置
  33.         sentinelProperties.getDatasource().values().forEach(this::registryWriter);
  34.     }
  35.     private void registryWriter(DataSourcePropertiesConfiguration dataSourceProperties) {
  36.    
  37.    
  38.         // 只获取application.yml文件中 nacos配置的数据源
  39.         final NacosDataSourceProperties nacosDataSourceProperties = dataSourceProperties.getNacos();
  40.         if (nacosDataSourceProperties == null) {
  41.    
  42.    
  43.             return;
  44.         }
  45.         // 获取规则类型,然后根据各个类型创建相应的写数据源
  46.         final RuleType ruleType = nacosDataSourceProperties.getRuleType();
  47.         switch (ruleType) {
  48.    
  49.    
  50.             case FLOW:
  51.                 WritableDataSource<List<FlowRule>> flowRuleWriter = new NacosWritableDataSource<>(nacosDataSourceProperties, JSON::toJSONString);
  52.                 WritableDataSourceRegistry.registerFlowDataSource(flowRuleWriter);
  53.                 break;
  54.             case DEGRADE:
  55.                 WritableDataSource<List<DegradeRule>> degradeRuleWriter = new NacosWritableDataSource<>(nacosDataSourceProperties, JSON::toJSONString);
  56.                 WritableDataSourceRegistry.registerDegradeDataSource(degradeRuleWriter);
  57.                 break;
  58.             case PARAM_FLOW:
  59.                 WritableDataSource<List<ParamFlowRule>> paramFlowRuleWriter = new NacosWritableDataSource<>(nacosDataSourceProperties, JSON::toJSONString);
  60.                 ModifyParamFlowRulesCommandHandler.setWritableDataSource(paramFlowRuleWriter);
  61.                 break;
  62.             case SYSTEM:
  63.                 WritableDataSource<List<SystemRule>> systemRuleWriter = new NacosWritableDataSource<>(nacosDataSourceProperties, JSON::toJSONString);
  64.                 WritableDataSourceRegistry.registerSystemDataSource(systemRuleWriter);
  65.                 break;
  66.             case AUTHORITY:
  67.                 WritableDataSource<List<AuthorityRule>> authRuleWriter = new NacosWritableDataSource<>(nacosDataSourceProperties, JSON::toJSONString);
  68.                 WritableDataSourceRegistry.registerAuthorityDataSource(authRuleWriter);
  69.                 break;
  70.             default:
  71.                 break;
  72.         }
  73.     }
  74. }
复制代码
  1. import com.alibaba.cloud.sentinel.datasource.config.NacosDataSourceProperties;
  2. import com.alibaba.csp.sentinel.datasource.Converter;
  3. import com.alibaba.csp.sentinel.datasource.WritableDataSource;
  4. import com.alibaba.nacos.api.NacosFactory;
  5. import com.alibaba.nacos.api.PropertyKeyConst;
  6. import com.alibaba.nacos.api.config.ConfigService;
  7. import com.alibaba.nacos.api.exception.NacosException;
  8. import lombok.extern.slf4j.Slf4j;
  9. import org.springframework.util.StringUtils;
  10. import java.util.Properties;
  11. import java.util.concurrent.locks.Lock;
  12. import java.util.concurrent.locks.ReentrantLock;
  13. /**
  14. * 将sentinel规则写入到nacos配置中心
  15. * @param <T>
  16. */
  17. @Slf4j
  18. public class NacosWritableDataSource<T> implements WritableDataSource<T> {
  19.    
  20.    
  21.     private final Converter<T, String> configEncoder;
  22.     private final NacosDataSourceProperties nacosDataSourceProperties;
  23.     private final Lock lock = new ReentrantLock(true);
  24.     private ConfigService configService = null;
  25.     public NacosWritableDataSource(NacosDataSourceProperties nacosDataSourceProperties, Converter<T, String> configEncoder) {
  26.    
  27.    
  28.         if (configEncoder == null) {
  29.    
  30.    
  31.             throw new IllegalArgumentException("Config encoder cannot be null");
  32.         }
  33.         if (nacosDataSourceProperties == null) {
  34.    
  35.    
  36.             throw new IllegalArgumentException("Config nacosDataSourceProperties cannot be null");
  37.         }
  38.         this.configEncoder = configEncoder;
  39.         this.nacosDataSourceProperties = nacosDataSourceProperties;
  40.         final Properties properties = buildProperties(nacosDataSourceProperties);
  41.         try {
  42.    
  43.    
  44.             // 也可以直接注入NacosDataSource,然后反射获取其configService属性
  45.             this.configService = NacosFactory.createConfigService(properties);
  46.         } catch (NacosException e) {
  47.    
  48.    
  49.             log.error("create configService failed.", e);
  50.         }
  51.     }
  52.     private Properties buildProperties(NacosDataSourceProperties nacosDataSourceProperties) {
  53.    
  54.    
  55.         Properties properties = new Properties();
  56.         if (!StringUtils.isEmpty(nacosDataSourceProperties.getServerAddr())) {
  57.    
  58.    
  59.             properties.setProperty(PropertyKeyConst.SERVER_ADDR, nacosDataSourceProperties.getServerAddr());
  60.         } else {
  61.    
  62.    
  63.             properties.setProperty(PropertyKeyConst.ACCESS_KEY, nacosDataSourceProperties.getAccessKey());
  64.             properties.setProperty(PropertyKeyConst.SECRET_KEY, nacosDataSourceProperties.getSecretKey());
  65.             properties.setProperty(PropertyKeyConst.ENDPOINT, nacosDataSourceProperties.getEndpoint());
  66.         }
  67.         if (!StringUtils.isEmpty(nacosDataSourceProperties.getNamespace())) {
  68.    
  69.    
  70.             properties.setProperty(PropertyKeyConst.NAMESPACE, nacosDataSourceProperties.getNamespace());
  71.         }
  72.         if (!StringUtils.isEmpty(nacosDataSourceProperties.getUsername())) {
  73.    
  74.    
  75.             properties.setProperty(PropertyKeyConst.USERNAME, nacosDataSourceProperties.getUsername());
  76.         }
  77.         if (!StringUtils.isEmpty(nacosDataSourceProperties.getPassword())) {
  78.    
  79.    
  80.             properties.setProperty(PropertyKeyConst.PASSWORD, nacosDataSourceProperties.getPassword());
  81.         }
  82.         return properties;
  83.     }
  84.     @Override
  85.     public void write(T value) throws Exception {
  86.    
  87.    
  88.         lock.lock();
  89.         // todo handle cluster concurrent problem
  90.         try {
  91.    
  92.    
  93.             String convertResult = configEncoder.convert(value);
  94.             if (configService == null) {
  95.    
  96.    
  97.                 log.error("configServer is null, can not continue.");
  98.                 return;
  99.             }
  100.             // 规则配置数据推送到nacos配置中心
  101.             final boolean published = configService.publishConfig(nacosDataSourceProperties.getDataId(), nacosDataSourceProperties.getGroupId(), convertResult);
  102.             if (!published) {
  103.    
  104.    
  105.                 log.error("sentinel {} publish to nacos failed.", nacosDataSourceProperties.getRuleType());
  106.             }
  107.         } finally {
  108.    
  109.    
  110.             lock.unlock();
  111.         }
  112.     }
  113.     @Override
  114.     public void close() throws Exception {
  115.    
  116.    
  117.     }
  118. }
复制代码


启动微服务举行测试。
dashboard中为某个接口界说一个流控规则



调用接口测试,发送三次哀求



检察Nacos中的设置文件,就会发现也乐成写入了



源码分析

读数据源

引入读数据源的依靠,我们来看看具体是怎么实现的
  1. <dependency>
  2.     <groupId>com.alibaba.csp</groupId>
  3.     <artifactId>sentinel-datasource-nacos</artifactId>
  4. </dependency>
复制代码


实现思绪:

      
  • 和文件的读数据源一样,继续了AbstractDataSource类,如许就不须要我们再去写一遍加载设置、更新内存中的设置
在源码中的这个扩展包下面,就有nacos读数据源的实现



我们先看看NacosDataSource类的父类的代码

      
  • 创建一个DynamicSentinelProperty对象,告急作用是更新内存中的规则设置  
  • 加载设置、分析设置
  1. public abstract class AbstractDataSource<S, T> implements ReadableDataSource<S, T> {
  2.    
  3.    
  4.     protected final Converter<S, T> parser;
  5.     protected final SentinelProperty<T> property;
  6.     public AbstractDataSource(Converter<S, T> parser)
复制代码
回复

使用道具 举报

登录后关闭弹窗

登录参与点评抽奖  加入IT实名职场社区
去登录
快速回复 返回顶部 返回列表