• Sentinel Dashboard 接入 Nacos 动态数据源 & Zuul 接入 Sentinel 实战


    背景

    Sentinel Dashboard 默认将限流、熔断等规则保存在内存中,然后同步给连接 Dashboard 的客户端,客户端也是保存在内存中。
    那么如果当 Sentinel Dashboard 异常重启,那么之前配置的规则将全部丢失,需要重新进行配置。
    其中,Sentinel 本身已经提供第三方组件作为动态数据源,如:Apollo、Nacos、Zookeeper。
    因此,我们可以对 Sentinel Dashboard 进行改造,将规则存储在上述的中间件中,保证 Dashboard 重启不会丢失原有的所有规则配置。

    改造

    下载源码

    因为我们需要对 Sentinel Dashboard 进行改造,所以需先下载源码下来,这里选择的版本为:1.8.6
    image.png
    下载源码后,我们可以发现在test下,是有 Apollo、Nacos 和 Zookeeper 作为数据源的代码,因此我们可以进行借鉴复用。
    image.png
    因为我们的微服务架构是:Spring Cloud Alibaba 系列的,所以注册中心和配置中心都是使用的 Nacos,所以下面将基于 Nacos 去改造 Sentinel Dashboard。

    Sentinel Dashboard 接入 Nacos

    test 中,Nacos 是通过调用 ConfigService 去查询规则和保存规则,虽然单元测试中代码仅仅是流控规则的代码,但是我们完全可以借鉴,毕竟现在仅仅是改造规则的保存和查询逻辑,整体方式都是一致的,仅仅需要找到其他规则所在代码,然后做替换即可。

    自定义 DynamicRuleProvider & DynamicRulePulisher

    DynamicRuleProvider

    用于查询规则,我们借鉴 test 代码中的com.alibaba.csp.sentinel.dashboard.rule.nacos.FlowRuleNacosProvider即可,为所有规则都实现一遍,核心逻辑就是使用 Nacos 提供的ConfigService进行查询。

    DynamicRulePublisher

    用于新增/更新/删除规则,我们借鉴 test 代码中的com.alibaba.csp.sentinel.dashboard.rule.nacos.FlowRuleNacosPublisher即可,为所有规则都实现一遍,核心逻辑就是使用 Nacos 提供的ConfigService进行推送。

    实现

    由于我们仅仅调整规则的保存/更新/删除和查询逻辑,其余业务逻辑不调整,所以不管是那种规则,调整的逻辑都是一致的,可能不一样的仅仅是以下两点:

    1. 保存在 Nacos 中,不同规则的dataId不一样
    2. 从 Nacos 查询后,转化的实体对象不一致

    因此,我们这里针对 Provider 和 Publisher 都抽象一个类,实现核心的保存逻辑和更新逻辑。不同规则只需要继承对应的抽象类,然后具体对应的规则dataId和查询实体泛型。
    NacosConfigUtil:

    /**
     * @author Eric Zhao
     * @since 1.4.0
     */
    public final class NacosConfigUtil {
    
        public static final String GROUP_ID = "SENTINEL_GROUP";
        // 保存到nacos中的文件后缀
        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 AUTHORITY_DATA_ID_POSTFIX = "-authority-rules";
        public static final String PARAM_FLOW_DATA_ID_POSTFIX = "-param-flow-rules";
        public static final String GATEWAY_FLOW_DATA_ID_POSTFIX = "-gw-flow-rules";
        public static final String GATEWAY_API_FLOW_DATA_ID_POSTFIX = "-gw-api-group-rules";
    
        //public static final String PARAM_FLOW_DATA_ID_POSTFIX = "-param-rules";
        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";
    
        private NacosConfigUtil() {}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32

    NacosRuleProvider:

    /**
     * Nacos RuleProvider 抽象类
     * @author winfun
     * @since 2023/9/29
     **/
    public abstract class NacosRuleProvider<T extends RuleEntity>  implements DynamicRuleProvider<List<T>> {
        @Autowired
        protected ConfigService configService;
        @Autowired
        protected Converter<String, List<T>> converter;
    
        @Override
        public List<T> getRules(String appName) throws Exception {
            String rules = configService.getConfig(appName + getDataIdPostfix(),
                    NacosConfigUtil.GROUP_ID, 3000);
            if (StringUtil.isEmpty(rules)) {
                return new ArrayList<>();
            }
            return converter.convert(rules);
        }
    
        /**
         * dataId 后缀,参考 com.alibaba.csp.sentinel.dashboard.rule.nacos.NacosConfigUtil
         * @return dataId 后缀
         */
        public abstract String getDataIdPostfix();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    NacosRulePublisher:

    /**
     * Nacos RulePublisher 抽象类
     * @author winfun
     * @since 2023/9/29
     **/
    public abstract class NacosRulePublisher<T extends RuleEntity> implements DynamicRulePublisher<List<T>> {
    
        @Autowired
        protected ConfigService configService;
        @Autowired
        protected Converter<List<T>, String> converter;
    
        @Override
        public void publish(String app, List<T> rules) throws Exception {
            AssertUtil.notEmpty(app, "app name cannot be empty");
            if (rules == null) {
                return;
            }
            configService.publishConfig(app + getDataIdPostfix(),
                    NacosConfigUtil.GROUP_ID, converter.convert(rules));
        }
        /**
         * dataId 后缀,参考 com.alibaba.csp.sentinel.dashboard.rule.nacos.NacosConfigUtil
         * @return dataId 后缀
         */
        public abstract String getDataIdPostfix();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    后续不同的规则,分别创建子类继承上面的抽象类即可。
    image.png
    这里拿网关做例子:

    /**
     * 网关 RulePublisher
     * @author winfun
     * @since 2023/9/29
     **/
    @Component("nacosGatewayFlowRulePublisher")
    public class NacosGatewayFlowRulePublisher extends NacosRulePublisher<GatewayFlowRuleEntity> {
    
        @Override
        public String getDataIdPostfix() {
            return NacosConfigUtil.GATEWAY_FLOW_DATA_ID_POSTFIX;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    /**
     * 网关 RuleProvider
     * @author winfun
     * @since 2023/9/29
     **/
    @Component("nacosGatewayFlowRuleProvider")
    public class NacosGatewayFlowRuleProvider extends NacosRuleProvider<GatewayFlowRuleEntity> {
    
        @Override
        public String getDataIdPostfix() {
            return NacosConfigUtil.GATEWAY_FLOW_DATA_ID_POSTFIX;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    Controller 接口调整

    上面,我们仅仅为各种规则的保存和查询创建了对应的 RulePublisher 和 RuleProvider,我们还需要对页面的接口进行调整,将接口中保存和查询规则的代码调整。
    image.png
    改动逻辑:将代码中使用 SentinelApiClient 进行规则查询和保存的逻辑代码,改为 Nacos 对应的 Provider 和 Publisher
    其中对应关系:

    1. GatewayApiController:使用 nacosApiDefinitionProvider 和 nacosApiDefinitionPublisher
    2. GatewayFlowRuleController:使用 nacosGatewayFlowRuleProvider 和 nacosGatewayFlowRulePublisher
    3. FlowControllerV2:使用 nacosFlowRuleProvider 和 nacosFlowRulePublisher
    4. AuthorityRuleController:使用 authorityRuleNacosProvider 和 authorityRuleNacosPublisher
    5. DegradeController:使用 nacosDegradeRuleProvider 和 nacosDegradeRulePublisher
    6. ParamFlowRuleController:使用 nacosParamFlowRuleProvider 和 nacosParamFlowRulePublisher
    7. SystemController:使用 nacosSystemRuleProvider 和 nacosSystemRulePublisher

    以 GatewayFlowRuleController 为例子:
    注释掉 SentinelApiClient,引入对应 Nacos 的 RulePublisher 和 RuleProvider:
    image.png
    查询代码调整:
    image.png
    新增/更新/删除代码调整,其实就是更新推送规则的逻辑:
    image.png

    启动应用

    添加 Nacos 配置:

    nacos.serverAddr=127.0.0.1:8848
    nacos.namespace=1ee594f6-001f-4d68-9852-ca6fce6039c9
    
    • 1
    • 2

    groupId默认使用 SENTINEL_GROUP,配置在 NacosConfigUtil 中

    启动 Spring 应用:
    image.png
    应用启动后,由于还没有 Sentinel 客户端接入,因此登录 Web 端后,是没有东西可以显示的。
    image.png

    Sentinel 客户端接入

    这里用 Zuul 网关服务作为例子。

    Zuul 网关接入 Sentinel

    引入Maven依赖:

    >
        >com.alibaba.csp>
        >sentinel-zuul-adapter>
    >
    >
        >com.alibaba.csp>
        >sentinel-transport-simple-http>
    >
    >
        >com.alibaba.cloud>
        >spring-cloud-starter-alibaba-sentinel>
    >
    >
        >com.alibaba.cloud>
        >spring-cloud-alibaba-sentinel-gateway>
    >
    
    <!-- 动态数据源 -->
    >
        >com.alibaba.cloud>
        >spring-cloud-alibaba-sentinel-datasource>
    >
    <!--  nacos 数据源 -->
    >
        >com.alibaba.csp>
        >sentinel-datasource-nacos>
    >
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    添加配置:

    spring:
      cloud:
        nacos:
          server-addr: 127.0.0.1:8848
          password: nacos
          username: nacos
          discovery:
            namespace: 1ee594f6-001f-4d68-9852-ca6fce6039c9
            enabled: true
          config:
            namespace: 1ee594f6-001f-4d68-9852-ca6fce6039c9
            enabled: false
            file-extension: yml
        sentinel:
          filter:
            enabled: true
          transport:
            port: 8719
            dashboard: 127.0.0.1:18181
          datasource:
            # 限流
            flow:
              nacos:
                server-addr: ${spring.cloud.nacos.server-addr}
                namespace: ${spring.cloud.nacos.discovery.namespace}
                dataId: ${spring.application.name}-flow-rules
                groupId: SENTINEL_GROUP
                data-type: json
                rule-type: flow
            # 熔断
            degrade:
              nacos:
                server-addr: ${spring.cloud.nacos.server-addr}
                namespace: ${spring.cloud.nacos.discovery.namespace}
                dataId: ${spring.application.name}-degrade-rules
                groupId: SENTINEL_GROUP
                data-type: json
                rule-type: degrade
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38

    自定义降级Provider:

    /**
     * CustomZuulBlockFallbackProvider
     * @author winfun
     * @since 2023/10/3
     **/
    @Slf4j
    @Component
    public class CustomZuulBlockFallbackProvider implements ZuulBlockFallbackProvider {
    
        @Override
        public String getRoute() {
            return "*";
        }
    
        @Override
        public BlockResponse fallbackResponse(String route, Throwable cause) {
            RequestContext ctx = RequestContext.getCurrentContext();
            HttpServletRequest request = ctx.getRequest();
            String url = request.getRequestURL().toString();
            if (cause instanceof FlowException) {
                log.warn("url:{} 触发限流",url);
                return new BlockResponse(429, "Sentinel触发限流", route);
            } else if (cause instanceof DegradeException) {
                log.warn("url:{} 触发熔断",url);
                return new BlockResponse(429, "Sentinel触发熔断", route);
            } else {
                return new BlockResponse(500, "系统异常", route);
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30

    启动应用,并随便调用一个接口,调用后 Sentinel 客户端就会将相关信息上报给 Sentinel Dashboard:
    image.png

    设置 Route 类型的限流规则和熔断规则

    在 Sentinel Dashboard 新增限流规则和熔断规则:
    image.png
    image.png
    Nacos 在对应的 namespace 中也会出现对应的配置:
    image.png
    调用报错的接口,当调用第一次失败后,第二次就会触发熔断规则:
    image.png

  • 相关阅读:
    旺店通·企业版对接打通金蝶云星空查询调拨单接口与分布式调入单新增接口
    Python之协程Coroutines
    【springboot】8、静态资源访问
    Springboot+高校教材预订信息管理系统 毕业设计-附源码150905
    使用Requests发送HTTP请求
    ES6及其后续版本的新特性的理解
    json文件批量转为txt文件
    DDD/ABP/EF Core 实现值对象Value Object
    最新小程序转app的神方案
    利用Python开发一个上传文件的服务
  • 原文地址:https://blog.csdn.net/Howinfun/article/details/133743430