sentinel 规则持久化


sentinel 规则持久化

             

官网:https://sentinelguard.io/zh-cn/docs/dynamic-rule-configuration.html

            

               

                                     

规则持久化

             

流控降级规则加载

# loadRules:从内存中读取,一般用于开发测试
FlowRuleManager.loadRules(List<FlowRule> rules);       //读取流控规则
DegradeRuleManager.loadRules(List<DegradeRule> rules); //读取降级规则

# DataSource接口:从文件、数据库、配置中心读取
控制台设置规则后,将规则推送到统一的规则中心(如nacos);
客户端实现ReadableDataSource接口监听规则中心,实时获取规则变更

    

dataSource接口:控制台将规则数据推送到配置中心,客户端动态读取规则数据

            

推模式:注册中心向客户端推送规则数据,客户端注册监听器动态获取最新数据,推模式实时性较好,如:zookeeper、nacos、redis、apollo、etcd等

拉模式:客户端定时轮询拉取规则数据,实时性较差,如:RDBMS、文件、consul、euraka等

             

***************

nacos 数据源

         

相关依赖

<dependency>
    <groupId>com.alibaba.csp</groupId>
    <artifactId>sentinel-datasource-nacos</artifactId>
    <version>x.y.z</version>
</dependency>

              

注册数据源

    @PostConstruct
    private static void loadRules() {
        ReadableDataSource<String, List<FlowRule>> flowRuleDataSource = new NacosDataSource<>(remoteAddress, groupId, dataId,
                source -> JSON.parseObject(source, new TypeReference<List<FlowRule>>() {
                }));
        FlowRuleManager.register2Property(flowRuleDataSource.getProperty());
    }

           

相关说明

该实现方式是将数据线写入nacos,客户端从nacos中读取数据,
后续nacos中数据如果发生变更,客户端也可以读取到最新的数据;

如果需要通过sentinel-dashboard中设置规则,并将规则推送到nacos中,
需要修改sentinel-dashboard源码,重新编译打包

           

                

                                     

相关类与接口

      

NacosDataSource

public class NacosDataSource<T> extends AbstractDataSource<String, T> {
    private static final int DEFAULT_TIMEOUT = 3000;
    private final ExecutorService pool;
    private final Listener configListener;
    private final String groupId;
    private final String dataId;
    private final Properties properties;
    private ConfigService configService;

    public NacosDataSource(String serverAddr, String groupId, String dataId, Converter<String, T> parser) {
    public NacosDataSource(final Properties properties, final String groupId, final String dataId, Converter<String, T> parser) {

            

AbstractDataSource

public abstract class AbstractDataSource<S, T> implements ReadableDataSource<S, T> {
    protected final Converter<S, T> parser;
    protected final SentinelProperty<T> property;

    public AbstractDataSource(Converter<S, T> parser) {
        if (parser == null) {
            throw new IllegalArgumentException("parser can't be null");
        } else {
            this.parser = parser;
            this.property = new DynamicSentinelProperty();
        }
    }

    public T loadConfig() throws Exception {
        return this.loadConfig(this.readSource());
    }

    public T loadConfig(S conf) throws Exception {
        T value = this.parser.convert(conf);
        return value;
    }

    public SentinelProperty<T> getProperty() {
        return this.property;
    }
}

             

ReadableDataSource

public interface ReadableDataSource<S, T> {
    T loadConfig() throws Exception;

    S readSource() throws Exception;

    SentinelProperty<T> getProperty();

    void close() throws Exception;
}

              

Converter

public interface Converter<S, T> {
    T convert(S var1);
}

            

               

JsonConverter

public class JsonConverter<T> extends SentinelConverter {
    public JsonConverter(ObjectMapper objectMapper, Class<T> ruleClass) {
        super(objectMapper, ruleClass);
    }
}

                     

XmlConverter

public class XmlConverter<T> extends SentinelConverter {
    public XmlConverter(XmlMapper xmlMapper, Class<T> ruleClass) {
        super(xmlMapper, ruleClass);
    }
}

         

SentinelConverter

public abstract class SentinelConverter<T> implements Converter<String, Collection<Object>> {
    private static final Logger log = LoggerFactory.getLogger(SentinelConverter.class);
    private final ObjectMapper objectMapper;
    private final Class<T> ruleClass;

    public SentinelConverter(ObjectMapper objectMapper, Class<T> ruleClass) {
        this.objectMapper = objectMapper;
        this.ruleClass = ruleClass;
    }

    public Collection<Object> convert(String source) {
        Object ruleCollection;
        if (this.ruleClass != FlowRule.class && this.ruleClass != DegradeRule.class && this.ruleClass != SystemRule.class && this.ruleClass != AuthorityRule.class && this.ruleClass != ParamFlowRule.class) {
            ruleCollection = new HashSet();
        } else {
            ruleCollection = new ArrayList();
        }

        if (StringUtils.isEmpty(source)) {
            log.warn("converter can not convert rules because source is empty");
            return (Collection)ruleCollection;
        } else {
            try {
                List sourceArray = (List)this.objectMapper.readValue(source, new TypeReference<List<HashMap>>() {
                });
                Iterator var4 = sourceArray.iterator();

                while(var4.hasNext()) {
                    Object obj = var4.next();
                    String item = null;

                    try {
                        item = this.objectMapper.writeValueAsString(obj);
                        Optional.ofNullable(this.convertRule(item)).ifPresent((convertRule) -> {
                            ruleCollection.add(convertRule);
                        });
                    } catch (IOException var8) {
                        log.error("sentinel rule convert error: " + var8.getMessage(), var8);
                        throw new IllegalArgumentException("sentinel rule convert error: " + var8.getMessage(), var8);
                    }
                }

                return (Collection)ruleCollection;
            } catch (Exception var9) {
                if (var9 instanceof RuntimeException) {
                    throw (RuntimeException)var9;
                } else {
                    throw new RuntimeException("convert error: " + var9.getMessage(), var9);
                }
            }
        }
    }

    private Object convertRule(String ruleStr) throws IOException {
        return this.objectMapper.readValue(ruleStr, this.ruleClass);
    }
}

            

                   

                                     

dashboard 源码修改

    

                              

说明:只修改了flowRule,其他规则如DegradeRule修改操作类似

              

修改pom.xml

        <!-- 添加注释,去除test属性 -->
        <!-- for Nacos rule publisher sample -->
        <dependency>
            <groupId>com.alibaba.csp</groupId>
            <artifactId>sentinel-datasource-nacos</artifactId>
            <!--<scope>test</scope>-->
        </dependency>

        <!-- 添加lombok依赖 -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.22</version>
            <scope>provided</scope>
        </dependency>

          

application.properties:添加nacos属性

# 自定义类NacosProperties读取属性
nacos.serverAddr=localhost:8848
#nacos.namespace=f94498e5-7384-40fc-a6fc-c7995c66bfd0

# 设置dashboard启动端口默认为8000
server.port=8000

         

NacosProperties:自定义属性类,读取nacos配置属性

@Data
@Component
@ConfigurationProperties(prefix = "nacos")
public class NacosProperties {

    private String serverAddr;
    private String groupId = "DEFAULT_GROUP";
    private String namespace = "";
}

          

NacosConstants:定义nacos常量

public class NacosConstants {

    public final static String GROUP_ID = "DEFAULT_GROUP";
    public final static String FLOW_RULE_DATAID_POSTFIX = "-flow-rule";
}

          

NacosConfig:创建converter、configService

@Configuration
public class NacosConfig {

    @Resource
    private NacosProperties nacosProperties;

    @Bean
    public Converter<List<FlowRuleEntity>, String> flowRuleEntityListToJSON(){
        return JSON::toJSONString;
    }

    @Bean
    public Converter<String, List<FlowRuleEntity>> jsonToFlowRuleEntityList(){
        return s -> JSON.parseArray(s, FlowRuleEntity.class);
    }

    @Bean("configService")
    public ConfigService initConfigService() throws NacosException {
        Properties properties = new Properties();
        properties.put(PropertyKeyConst.SERVER_ADDR, nacosProperties.getServerAddr());
        properties.put(PropertyKeyConst.NAMESPACE, nacosProperties.getNamespace());

        return ConfigFactory.createConfigService(properties);
    }
}

             

NacosFlowRulePublisher:向nacos中写入FlowRule规则

@Component("nacosFlowRulePublisher")
public class NacosFlowRulePublisher implements DynamicRulePublisher<List<FlowRuleEntity>>{

    @Resource
    private NacosProperties nacosProperties;

    @Resource(name = "configService")
    private ConfigService configService;

    @Resource
    private Converter<List<FlowRuleEntity>, String> converter;

    @Override
    public void publish(String app, List<FlowRuleEntity> rules) throws Exception {
        AssertUtil.notEmpty(app, "app不能为空");
        if (StringUtils.isEmpty(rules)){
            return;
        }

        String dataId = app + NacosConstants.FLOW_RULE_DATAID_POSTFIX;
        configService.publishConfig(dataId, nacosProperties.getGroupId(),
                converter.convert(rules), ConfigType.JSON.getType());
    }
}

              

NacosFlowRuleProvider:从nacos中读取flowRule规则

@Component("nacosFlowRuleProvider")
public class NacosFlowRuleProvider implements DynamicRuleProvider<List<FlowRuleEntity>>{

    @Resource
    private NacosProperties nacosProperties;

    @Resource
    private ConfigService configService;

    @Resource
    private Converter<String, List<FlowRuleEntity>> converter;

    @Override
    public List<FlowRuleEntity> getRules(String appName) throws Exception {
        String dataId = appName + NacosConstants.FLOW_RULE_DATAID_POSTFIX;
        String rules = configService.getConfig(dataId,nacosProperties.getGroupId(),10000);
        if (StringUtils.isEmpty(rules)){
            return new ArrayList<>();
        }

        return converter.convert(rules);
    }
}

           

FlowControllerV2:注入自定义的provider、piublisher

@RestController
@RequestMapping(value = "/v2/flow")
public class FlowControllerV2 {

    private final Logger logger = LoggerFactory.getLogger(FlowControllerV2.class);

    @Autowired
    private InMemoryRuleRepositoryAdapter<FlowRuleEntity> repository;

    @Autowired
    @Qualifier("nacosFlowRuleProvider")  //注入自定义的nacosFlowRuleProvider
    private DynamicRuleProvider<List<FlowRuleEntity>> ruleProvider;
    @Autowired
    @Qualifier("nacosFlowRulePublisher") //注入自定义的nacosFlowRulePublisher
    private DynamicRulePublisher<List<FlowRuleEntity>> rulePublisher;

            

sidebar.html:调用v2版本的脚本

          <!-- 注释掉该块代码 -->
          <!--
          <li ui-sref-active="active" ng-if="!entry.isGateway">
            <a ui-sref="dashboard.flowV1({app: entry.app})">
              <i class="glyphicon glyphicon-filter"></i>&nbsp;&nbsp;流控规则</a>
          </li>
          -->

          <!-- 调用dashboard.flow -->
          <li ui-sref-active="active" ng-if="!entry.isGateway">
            <a ui-sref="dashboard.flow({app: entry.app})">
              <i class="glyphicon glyphicon-filter"></i>&nbsp;&nbsp;流控规则</a>
          </li>

          

identity.js:FlowServiceV1 ==> FlowServiceV2

            

               

flow_v2.html:注释掉代码块

            

               

                

                                     

使用示例

  

                              

            

application.yml

spring:
  application:
    name: hello-service
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848
      config:
        server-addr: localhost:8848
        prefix: ${spring.application.name}-flow-rule
    sentinel:
      transport:
        dashboard: localhost:8000

         

CustomDataSourceConfig:注册nacos数据源,进行规则读写

@Configuration
public class CustomDataSourceConfig {

    @Resource
    private NacosConfigProperties nacosConfigProperties;

    @PostConstruct
    public void init(){
        ReadableDataSource<String, List<FlowRule>> flowRuleDataSource = new NacosDataSource<>(nacosConfigProperties.getServerAddr(),
                nacosConfigProperties.getGroup(), nacosConfigProperties.getPrefix(),
                source -> JSON.parseObject(source, new TypeReference<List<FlowRule>>() {
                }));
        FlowRuleManager.register2Property(flowRuleDataSource.getProperty());
    }
}

        

HelloController

@RestController
public class HelloController {

    @RequestMapping("/hello")
    public String hello(){
        return "hello";
    }
}

            

***************

使用测试

      

localhost:8000:sentinel dashboard,定义流控规则

            

            

             

localhost:8848/nacos:nacos查看流控规则

            

            

            

sentinel dashboard修改流控规则

            

              

             

nacos 查看修改后的规则数据

            

             

jmeter 限流测试

            

            

            

            

        

             

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值