返回文章列表

生产环境Sentinel改造实践(二):规则管理推送改造

分类
标签
SilentBlade
SilentBlade
2023-01-11 2 分钟阅读 27 阅读

前文介绍了 Sentinel 的核心概念,本文开始动手对规则管理与推送机制进行改造,并以“流控规则”为例进行具体实现。

Sentinel Dashboard 改造

  1. com.alibaba.csp.sentinel.dashboard.rule.nacos 包下新建 NacosConfig.java

    java
    package com.alibaba.csp.sentinel.dashboard.rule.nacos;
    
    import com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.FlowRuleEntity;
    import com.alibaba.csp.sentinel.datasource.Converter;
    import com.alibaba.fastjson.JSON;
    import com.alibaba.nacos.api.PropertyKeyConst;
    import com.alibaba.nacos.api.config.ConfigFactory;
    import com.alibaba.nacos.api.config.ConfigService;
    import lombok.Getter;
    import lombok.Setter;
    import org.springframework.boot.context.properties.ConfigurationProperties;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    
    import java.util.List;
    import java.util.Properties;
    
    /**
     * @wiki https://github.com/eacdy/Sentinel-Dashboard-Nacos
     * add by tam
     */
    @Configuration
    public class NacosConfig {
    
        @Bean
        public Converter<List<FlowRuleEntity>, String> flowRuleEntityEncoder() {
            return JSON::toJSONString;
        }
    
        @Bean
        public Converter<String, List<FlowRuleEntity>> flowRuleEntityDecoder() {
            return s -> JSON.parseArray(s, FlowRuleEntity.class);
        }
    
        @Bean
        public ConfigService nacosConfigService() throws Exception {
            NacosProperties nacosProperties = nacosProperties();
            Properties properties = new Properties();
            properties.put(PropertyKeyConst.SERVER_ADDR, nacosProperties.getServerAddr());
            if (!nacosProperties.getNamespace().isEmpty()) {
                properties.put(PropertyKeyConst.NAMESPACE, nacosProperties.getNamespace());
            }
            return ConfigFactory.createConfigService(properties);
        }
    
        @Bean
        @ConfigurationProperties(prefix = "spring.cloud.sentinel.datasource.nacos")
        public NacosProperties nacosProperties() {
            return new NacosProperties();
        }
    
    
        @Getter
        @Setter
        public static class NacosProperties {
            private String serverAddr;
            private String namespace;
            private String groupId;
        }
    }
    

    这里主要接收三个参数:serverAddr(Nacos 服务器地址)、namespace(Nacos 命名空间)和 groupId(Nacos 分组)。

    接着在 application.properties 中新增以下配置:

    properties
    spring.cloud.sentinel.datasource.nacos.server-addr=172.16.1.20:8848
    spring.cloud.sentinel.datasource.nacos.groupId=DEFAULT_GROUP
    spring.cloud.sentinel.datasource.nacos.namespace=wuyan_local
    
  2. 在同级目录下新增 NacosConfigUtil.java

    java
    package com.alibaba.csp.sentinel.dashboard.rule.nacos;
    
    import com.alibaba.csp.sentinel.dashboard.util.JSONUtils;
    import com.alibaba.nacos.api.config.ConfigService;
    import com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.RuleEntity;
    import com.alibaba.fastjson.JSON;
    import com.alibaba.csp.sentinel.slots.block.Rule;
    import com.alibaba.csp.sentinel.util.AssertUtil;
    import com.alibaba.csp.sentinel.util.StringUtil;
    import com.alibaba.nacos.api.exception.NacosException;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Component;
    
    import java.util.ArrayList;
    import java.util.List;
    import java.util.stream.Collectors;
    
    /**
     * @wiki https://github.com/eacdy/Sentinel-Dashboard-Nacos
     * add by tam
     */
    @Component
    public class NacosConfigUtil {
        @Autowired
        private NacosConfig.NacosProperties nacosProperties;
    
        public static final String FLOW_DATA_ID_POSTFIX = "-flow-rules";
        public static final String DEGRADE_DATA_ID_POSTFIX = "-degrade-rules";
        public static final String SYSTEM_DATA_ID_POSTFIX = "-system-rules";
        public static final String PARAM_FLOW_DATA_ID_POSTFIX = "-param-flow-rules";
        public static final String AUTHORITY_DATA_ID_POSTFIX = "-authority-rules";
        public static final String GATEWAY_FLOW_DATA_ID_POSTFIX = "-gateway-flow-rules";
        public static final String GATEWAY_API_DATA_ID_POSTFIX = "-gateway-api-rules";
        public static final String DASHBOARD_POSTFIX = "-dashboard";
        public static final String CLUSTER_MAP_DATA_ID_POSTFIX = "-cluster-map";
    
        /**
         * cc for `cluster-client`
         */
        public static final String CLIENT_CONFIG_DATA_ID_POSTFIX = "-cc-config";
        /**
         * cs for `cluster-server`
         */
        public static final String SERVER_TRANSPORT_CONFIG_DATA_ID_POSTFIX = "-cs-transport-config";
        public static final String SERVER_FLOW_CONFIG_DATA_ID_POSTFIX = "-cs-flow-config";
        public static final String SERVER_NAMESPACE_SET_DATA_ID_POSTFIX = "-cs-namespace-set";
    
        /**
         * 将规则序列化成JSON文本,存储到Nacos server中
         *
         * @param configService nacos config service
         * @param app           应用名称
         * @param postfix       规则后缀 eg.NacosConfigUtil.FLOW_DATA_ID_POSTFIX
         * @param rules         规则对象
         * @throws NacosException 异常
         */
        public <T> void setRuleStringToNacos(ConfigService configService, String app, String postfix, List<T> rules) throws NacosException {
            AssertUtil.notEmpty(app, "app name cannot be empty");
            if (rules == null) {
                return;
            }
    
            List<Rule> ruleForApp = rules.stream()
                    .map(rule -> {
                        RuleEntity rule1 = (RuleEntity) rule;
                        //System.out.println(rule1.getClass());
                        Rule rule2 = rule1.toRule();
                        //System.out.println(rule2.getClass());
                        return rule2;
                    })
                    .collect(Collectors.toList());
    
            // 存储,给微服务使用
            String dataId = genDataId(app, postfix);
            configService.publishConfig(
                    dataId,
                    nacosProperties.getGroupId(),
                    JSON.toJSONString(ruleForApp)
            );
    
            // 存储,给控制台使用
            configService.publishConfig(
                    dataId + DASHBOARD_POSTFIX,
                    nacosProperties.getGroupId(),
                    JSONUtils.toJSONString(rules)
            );
        }
    
        /**
         * 从Nacos server中查询响应规则,并将其反序列化成对应Rule实体
         *
         * @param configService nacos config service
         * @param appName       应用名称
         * @param postfix       规则后缀 eg.NacosConfigUtil.FLOW_DATA_ID_POSTFIX
         * @param clazz         类
         * @param <T>           泛型
         * @return 规则对象列表
         * @throws NacosException 异常
         */
        public <T> List<T> getRuleEntitiesFromNacos(ConfigService configService, String appName, String postfix, Class<T> clazz) throws NacosException {
            String rules = configService.getConfig(
                    genDataId(appName, postfix) + DASHBOARD_POSTFIX,
                    //genDataId(appName, postfix),
                    nacosProperties.getGroupId(),
                    3000
            );
            if (StringUtil.isEmpty(rules)) {
                return new ArrayList<>();
            }
            return JSONUtils.parseObject(clazz, rules);
        }
    
        private static String genDataId(String appName, String postfix) {
            return appName + postfix;
        }
    

    注意:完整的 dataId 规则为 [客户端 spring.application.name 配置的应用名] + -flow-rules``。

    这一点非常关键,客户端在集成 Nacos 与 Sentinel 配置时必须保持一致。

  3. com.alibaba.csp.sentinel.dashboard.rule.nacos.flow 目录下新建 FlowRuleNacosProvider.java

    java
    package com.alibaba.csp.sentinel.dashboard.rule.nacos.flow;
    
    import com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.FlowRuleEntity;
    import com.alibaba.csp.sentinel.dashboard.rule.DynamicRuleProvider;
    import com.alibaba.csp.sentinel.dashboard.rule.nacos.NacosConfigUtil;
    import com.alibaba.nacos.api.config.ConfigService;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Component;
    
    import java.util.List;
    
    /**
     * @author Eric Zhao
     * @since 1.4.0
     * add by tam
     */
    @Component("flowRuleNacosProvider")
    public class FlowRuleNacosProvider implements DynamicRuleProvider<List<FlowRuleEntity>> {
    
        @Autowired
        private ConfigService configService;
    
        @Autowired
        private NacosConfigUtil nacosConfigUtil;
    
        @Override
        public List<FlowRuleEntity> getRules(String appName) throws Exception {
            return nacosConfigUtil.getRuleEntitiesFromNacos(
                    this.configService,
                    appName,
                    NacosConfigUtil.FLOW_DATA_ID_POSTFIX,
                    FlowRuleEntity.class
            );
        }
    }
    

    该类的主要作用是从 Nacos 拉取规则信息。

  4. com.alibaba.csp.sentinel.dashboard.rule.nacos.flow 目录下新建 FlowRuleNacosPublisher.java

    java
    package com.alibaba.csp.sentinel.dashboard.rule.nacos.flow;
    
    import com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.FlowRuleEntity;
    import com.alibaba.csp.sentinel.dashboard.rule.DynamicRulePublisher;
    import com.alibaba.csp.sentinel.dashboard.rule.nacos.NacosConfigUtil;
    import com.alibaba.nacos.api.config.ConfigService;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Component;
    
    import java.util.List;
    
    /**
     * @author Eric Zhao
     * @since 1.4.0
     * add by tam
     */
    @Component("flowRuleNacosPublisher")
    public class FlowRuleNacosPublisher implements DynamicRulePublisher<List<FlowRuleEntity>> {
    
        @Autowired
        private ConfigService configService;
    
        @Autowired
        private NacosConfigUtil nacosConfigUtil;
    
        @Override
        public void publish(String app, List<FlowRuleEntity> rules) throws Exception {
            nacosConfigUtil.setRuleStringToNacos(
                    this.configService,
                    app,
                    NacosConfigUtil.FLOW_DATA_ID_POSTFIX,
                    rules
            );
        }
    }
    
  5. 修改 com.alibaba.csp.sentinel.dashboard.controller.v2.FlowControllerV2 类:

    java
    	@Autowired
        @Qualifier("flowRuleNacosProvider")
        private DynamicRuleProvider<List<FlowRuleEntity>> ruleProvider;
        @Autowired
        @Qualifier("flowRuleNacosPublisher")
        private DynamicRulePublisher<List<FlowRuleEntity>> rulePublisher;
    

    主要变更为增加 @Qualifier("flowRuleNacosProvider")@Qualifier("flowRuleNacosPublisher") 注解注入。

Sentinel 客户端接入

如果是 Spring Cloud 项目,请在 pom.xml 中引入以下依赖:

xml
		<dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
        </dependency>

        <dependency>
            <groupId>com.alibaba.csp</groupId>
            <artifactId>sentinel-datasource-nacos</artifactId>
        </dependency>

bootstrap.properties 中新增如下配置:

properties
server.port=8002
spring.application.name=qetesh-openapi
spring.cloud.nacos.discovery.server-addr=172.16.1.20:8848
spring.cloud.nacos.discovery.namespace=wuyan_local

spring.cloud.sentinel.transport.dashboard=localhost:8888
spring.cloud.sentinel.eager=true
spring.cloud.sentinel.datasource.ds1.nacos.server-addr=127.0.0.1:8848
spring.cloud.sentinel.datasource.ds1.nacos.dataId=${spring.application.name}-flow-rules
spring.cloud.sentinel.datasource.ds1.nacos.namespace=wuyan_local
spring.cloud.sentinel.datasource.ds1.nacos.groupId=DEFAULT_GROUP
spring.cloud.sentinel.datasource.ds1.nacos.rule-type=flow

注意:serverAddrnamespacegroupId 三个参数需与 Dashboard 保持一致,同时 spring.cloud.sentinel.datasource.ds1.nacos.rule-type 的值应设为 flow(流控)。

在启动类中增加测试代码:

java
import com.alibaba.csp.sentinel.annotation.SentinelResource;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@SpringBootApplication
@EnableDiscoveryClient
public class QeteshOpenapiApplication {

    public static void main(String[] args) {
        SpringApplication.run(QeteshOpenapiApplication.class, args);
    }

    @RestController
    static class TestController {

        @SentinelResource("ken2")
        @GetMapping("/hello")
        public String hello() {
            return "itweek.top";
        }

    }

}

启动项目并访问接口,即可在 Dashboard 中看到相关配置:

DashBoard列表

在流控规则页面新增如下配置:

流控规则新增配置

流控规则

此时,Nacos 后台也会同步生成相应的配置信息:

nacos配置

多次访问接口触发限流,即代表流控规则改造成功:

流控规则限流成功

评论 0

使用 GitHub 身份参与讨论

可以先写评论,登录后草稿会自动恢复。

访
暂无评论,来发表第一条评论吧。