Sentinel的gateway规则持久化改造

每天多学一点点~
话不多说,这就开始吧…

1. 前言

上篇写过 Sentinel规则持久化改造(pull+push+Ahas),但一般springcloud项目都是与zuul,gateway等网关结合使用,今儿总结下 sentinel 与 gateway的持久化改造。
以sentinel 1.7.0 版本 为例 sentinel git地址

2. 源码改造

2.1 老规矩,去掉sentinel-dashboard 工程的pom中test

<!-- 去掉test -->
<dependency>
    <groupId>com.alibaba.csp</groupId>
    <artifactId>sentinel-datasource-nacos</artifactId>
   <!-- <scope>test</scope>-->
</dependency>

2.2 复制 test下 NacosConfig类并修改

修改 Converter<List 中的 FlowRuleEntity规则

在上篇基础上,加入网关的API和FlowRule

@Configuration
public class NacosConfig {
  /**
     * 流控规则
     *
     * @return
     */
    @Bean
    public Converter<List<FlowRuleEntity>, String> flowRuleEntityEncoder() {
        return JSON::toJSONString;
    }

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

    /**
     * 授权规则
     *
     * @return
     */
    @Bean
    public Converter<List<AuthorityRuleEntity>, String> authorRuleEntityEncoder() {
        return JSON::toJSONString;
    }

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

    /**
     * 降级规则
     *
     * @return
     */
    @Bean
    public Converter<List<DegradeRuleEntity>, String> degradeRuleEntityEncoder() {
        return JSON::toJSONString;
    }

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

    /**
     * 热点参数 规则
     *
     * @return
     */
    @Bean
    public Converter<List<ParamFlowRuleEntity>, String> paramRuleEntityEncoder() {
        return JSON::toJSONString;
    }

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

    /**
     * 系统规则
     *
     * @return
     */
    @Bean
    public Converter<List<SystemRuleEntity>, String> systemRuleEntityEncoder() {
        return JSON::toJSONString;
    }

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

    /**
     * 网关API
     *
     * @return
     * @throws Exception
     */
    @Bean
    public Converter<List<ApiDefinitionEntity>, String> apiDefinitionEntityEncoder() {
        return JSON::toJSONString;
    }

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

    /**
     * 网关flowRule
     *
     * @return
     * @throws Exception
     */
    @Bean
    public Converter<List<GatewayFlowRuleEntity>, String> gatewayFlowRuleEntityEncoder() {
        return JSON::toJSONString;
    }

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

    @Bean
    public ConfigService nacosConfigService() throws Exception {
        return ConfigFactory.createConfigService("47.111.191.111");
    }
}

2.3 新增 pulisher 推送规则 和 provider 拉取规则

在这里插入图片描述
**GetWayApiNacosProvider 拉取API **

@Component("getWayApiNacosProvider")
public class GetWayApiNacosProvider implements DynamicRuleProvider<List<ApiDefinitionEntity>> {
    @Autowired
    private ConfigService configService;
    @Autowired
    private Converter<String , List<ApiDefinitionEntity>> converter;
    @Override
    public List<ApiDefinitionEntity> getRules(String appName) throws Exception {
        String rules = configService.getConfig(appName+ NacosConfigUtil.GETWAY_API_DATA_ID_POSTFIX
        ,NacosConfigUtil.GROUP_ID,3000);
        if(StringUtil.isEmpty(rules)){
            return new ArrayList<>();
        }
        return converter.convert(rules);
    }
}

GetWayApiNacosPublisher 推送API

@Component("getWayApiNacosPublisher")
public class GetWayApiNacosPublisher implements DynamicRulePublisher<List<ApiDefinitionEntity>> {
    @Autowired
    private ConfigService configService;
    @Autowired
    private Converter<List<ApiDefinitionEntity>, String> converter;
    @Override
    public void publish(String app, List<ApiDefinitionEntity> rules) throws Exception {
        AssertUtil.notEmpty(app, "app name cannot be empty");
        if (rules == null) {
            return;
        }
        configService.publishConfig(app+ NacosConfigUtil.GETWAY_API_DATA_ID_POSTFIX,
                NacosConfigUtil.GROUP_ID,converter.convert(rules));
    }
}

GateWayFlowRulesNacosProvider 拉取规则

@Component("gateWayFlowRulesNacosProvider")
public class GateWayFlowRulesNacosProvider implements DynamicRuleProvider<List<GatewayFlowRuleEntity>> {

    @Autowired
    private ConfigService configService;
    @Autowired
    private Converter<String, List<GatewayFlowRuleEntity>> converter;

    @Override
    public List<GatewayFlowRuleEntity> getRules(String appName) throws Exception {
        String rules = configService.getConfig(appName + NacosConfigUtil.GETWAY_FLOW_DATA_ID_POSTFIX,
                NacosConfigUtil.GROUP_ID, 3000);
        if (StringUtil.isEmpty(rules)) {
            return new ArrayList<>();
        }
        return converter.convert(rules);
    }

}

GateWayFlowRulesNacosPunlisher 推送规则

@Component("gateWayFlowRulesNacosPunlisher")
public class GateWayFlowRulesNacosPunlisher implements DynamicRulePublisher<List<GatewayFlowRuleEntity>> {

    @Autowired
    private ConfigService configService;
    @Autowired
    private Converter<List<GatewayFlowRuleEntity>, String> converter;


    @Override
    public void publish(String app, List<GatewayFlowRuleEntity> rules) throws Exception {
        AssertUtil.notEmpty(app, "app name cannot be empty");
        if (rules == null) {
            return;
        }
        configService.publishConfig(app + NacosConfigUtil.GETWAY_FLOW_DATA_ID_POSTFIX,
                NacosConfigUtil.GROUP_ID, converter.convert(rules));
    }
}

2.4 修改GatewayFlowRuleController 和 GatewayApiController

  1. 注入上文我们自己写的 GetWayApiNacosProvider 和 GetWayApiNacosPublisher
//添加我们自己写的ruleProvider
@Autowired
@Qualifier("gateWayFlowRuleNacosProvider")
private DynamicRuleProvider<List<GatewayFlowRuleEntity>> ruleProvider;

//添加我们自己写的 publisher
@Autowired
@Qualifier("gateWayFlowRuleNacosPubisher")
private DynamicRulePublisher<List<GatewayFlowRuleEntity>> rulePublisher;
  1. 以 @GetMapping("/list.json") 为例,修改代码
// 原生方法 拉取网关微服务
// List<GatewayFlowRuleEntity> rules = sentinelApiClient.fetchGatewayFlowRules(app, ip, port).get();
// 现修改 ---> 去配置中心拉取
List<GatewayFlowRuleEntity> rules = ruleProvider.getRules(app);

其他 Mapping 同理

  1. 新增方法,发布到nacos上
GatewayApiController 类
   /**
     *  api 推送 到 nacos
     * @param app
     * @throws Exception
     */
    private void publishApis(String app) throws Exception {
        List<ApiDefinitionEntity> rules = repository.findAllByApp(app);
        rulePublisher.publish(app,rules);
    }

GatewayFlowRuleController 类
  /**
     * 把配置推给nacos中
     *
     * @param app
     * @throws Exception
     */
    private void publishRules(String app) throws Exception {
        List<GatewayFlowRuleEntity> rules = repository.findAllByApp(app);
        rulePublisher.publish(app, rules);
    }
  1. 修改GatewayApiController 和 GatewayFlowRuleController 的 @PostMapping("/new.json") @PostMapping("/save.json") @PostMapping("/delete.json") 都是一样的改造
entity = repository.save(entity); // 保存到内存中
// 推送给微服务
//  publishRules(entity.getApp(), entity.getIp(), entity.getPort());
// 现通过 自己的publisher推送到配置中心
publishRules(entity.getApp());
  1. 修改 NacosConfigUtil
public final class NacosConfigUtil {

    public static final String GROUP_ID = "SENTINEL_GROUP";
    
    public static final String FLOW_DATA_ID_POSTFIX = "-flow-rules";
    public static final String DEGRAD_DATA_ID_POSTFIX = "-degrade-rules";
    public static final String AUTH_DATA_ID_POSTFIX = "-authority-rules";
    public static final String SYS_DATA_ID_POSTFIX = "-system-rules";
    public static final String PARAM_FLOW_DATA_ID_POSTFIX = "-param-rules";
    public static final String CLUSTER_MAP_DATA_ID_POSTFIX = "-cluster-map";
    public static final String GETWAY_API_DATA_ID_POSTFIX = "-getway-api";
    public static final String GETWAY_FLOW_DATA_ID_POSTFIX = "-getway-flow";

    /**
     * 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() {}
}

此时,GatewayFlowRuleController 已经改造完毕。喜大普奔23333

2.5 修改端口

如果是集群,修改为nginx的地址
在这里插入图片描述

3. 微服务网关改造

  1. 加入依赖
<!-- sentilen用 nacos 做 持久化-->
<dependency>
   <groupId>com.alibaba.csp</groupId>
   <artifactId>sentinel-datasource-nacos</artifactId>
</dependency>
  1. yml配置
# 新增持久化配置 
server:
  port: 8888
spring:
  application:
    name: gateway-sentinel
  cloud:
    gateway:
      discovery:
        locator:
          lower-case-service-id: true
          enabled: true
      routes:
        - id: product_center
          uri: lb://product-center
          predicates:
            - Path=/product/**     
        - id: order_center
          uri: lb://order-center
          predicates:
            - Path=/order/**
    nacos:
      discovery:
        server-addr: localhost:8848
    sentinel:
      transport:
        dashboard: localhost:9999
      datasource:
        # 名称随意
        gw-flow:
          nacos:
            server-addr: localhost:8848
            dataId: ${spring.application.name}-gateway-flow # 在修改的sentinel 源码中定义的规则名
            groupId: SENTINEL_GROUP
            rule-type: gw-flow
        gw-api-group:
          nacos:
            server-addr: localhost:8848
            dataId: ${spring.application.name}-gateway-api # 在修改的sentinel 源码中定义的规则名
            groupId: SENTINEL_GROUP
            rule-type: gw-api-group
  main:
    allow-bean-definition-overriding: true

4. 持久化测试

启动 订单微服务工程(啥也没有,就是一个接口查db) 和网关微服务工程,启动sentinel源码 地址 http://localhost:9999/#/login

先访问一次网关接口 http://localhost:8888/product/selectProductInfoById/1

http://localhost:9999/#/dashboard (sentinel 控制台)
在这里插入图片描述

配置流控

再看nacos
在这里插入图片描述

多次访问 网关接口被限流了

最后,重启 gateway or sentinel 规则还在~ 在nacos或者sentinel控制台 改规则,两边都能同步
继续多次刷网关接口,显示被限流

大功告成~

5.结语

世上无难事,只怕有心人,每天积累一点点,fighting!!!

Logo

权威|前沿|技术|干货|国内首个API全生命周期开发者社区

更多推荐