侧边栏壁纸
博主头像
qingtian博主等级

喜欢是一件细水流长的事,是永不疲惫的双向奔赴~!

  • 累计撰写 104 篇文章
  • 累计创建 48 个标签
  • 累计收到 1 条评论

使用SpringCloudAlibaba的Gateway自动配置

qingtian
2021-12-13 / 0 评论 / 1 点赞 / 1,074 阅读 / 7,075 字 / 正在检测是否收录...
温馨提示:
本文最后更新于 2021-12-13,若内容或图片失效,请留言反馈。部分素材来自网络,若不小心影响到您的利益,请联系我们删除。

使用Nacos作为配置中心实现Gateway配置的自动更新

使用Nacos做为配置中心

  • bootstrap.yml中配置好nacos

    nacos:
      gateway:
        route:
          config:
            data-id: e-commerce-gateway-router
            group: e-commerce
    
  • 创建NacosConfig配置类读取bootstrap.yml中的属性

    /**
     * @author qingtian
     * @description:配置类,读取Nacos中的配置项,用于配置监听器
     * @Package com.imooc.ecommerce.config
     * @date 2021/12/7 0:41
     */
    @Configuration
    public class GatewayConfig {
    
        /**
         * 读取配置的超时时间
         */
        public static final long DEFAULT_TIMEOUT = 30000;
    
        /**
         * Nacos服务器地址
         */
        public static String NACOS_SERVER_ADDR;
    
        /**
         * 命名空间
         */
        public static String NACOS_NAMESPACE;
    
        /**
         * data_id
         */
        public static String NACOS_ROUTER_DATA_ID;
    
        /**
         * 分组id
         */
        public static String NACOS_ROUTER_GROUP;
    
    
        @Value("${spring.cloud.nacos.discovery.server-addr}")
        public void setNacosServerAddr(String nacosServerAddr) {
            NACOS_SERVER_ADDR = nacosServerAddr;
        }
    
        @Value("${spring.cloud.nacos.discovery.namespace}")
        public void setNacosNamespace(String nacosNamespace) {
            NACOS_NAMESPACE = nacosNamespace;
        }
    
        @Value("${nacos.gateway.route.config.data-id}")
        public void setNacosRouterDataId(String nacosRouterDataId) {
            NACOS_ROUTER_DATA_ID = nacosRouterDataId;
        }
    
        @Value("${nacos.gateway.route.config.group}")
        public void setNacosRouterGroup(String nacosRouterGroup) {
            NACOS_ROUTER_GROUP = nacosRouterGroup;
        }
    
    }
    

创建DynamicRouteServiceImpl

/**
 * @author qingtian
 * @description:事件推送 Aware :动态更新路由网关 Service
 * @Package com.imooc.ecommerce.config
 * @date 2021/12/7 15:55
 */
@Slf4j
@Service
public class DynamicRouteServiceImpl implements ApplicationEventPublisherAware {

    /**
     * 写路由定义
     */
    private final RouteDefinitionWriter routeDefinitionWriter;

    /**
     * 获取路由定义
     */
    private final RouteDefinitionLocator routeDefinitionLocator;

    /**
     * 事件发布
     */
    private ApplicationEventPublisher publisher;

    public DynamicRouteServiceImpl(RouteDefinitionWriter routeDefinitionWriter
                        ,RouteDefinitionLocator routeDefinitionLocator) {
        this.routeDefinitionWriter = routeDefinitionWriter;
        this.routeDefinitionLocator = routeDefinitionLocator;
    }

    @Override
    public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {

        this.publisher = applicationEventPublisher;
    }

    /**
     * 增加路由定义
     * @param routeDefinition
     * @return
     */
    public String addRouteDefinition(RouteDefinition routeDefinition) {

        log.info("gateway add route : [{}]",routeDefinition);

        //保存路由配置并发布
        routeDefinitionWriter.save(Mono.just(routeDefinition)).subscribe();
        //发布事件通知给gateway
        this.publisher.publishEvent(new RefreshRoutesEvent(this));

        return "success";
    }

    /**
     * 根据路由id删除路由
     * @param id
     * @return
     */
    private String deleteById(String id) {

        try {
            log.info("gateway delete route id : [{}]",id);
            routeDefinitionWriter.delete(Mono.just(id)).subscribe();
            this.publisher.publishEvent(new RefreshRoutesEvent(this));
            return "success";
        } catch (Exception ex) {
            log.error("gateway delete route fail : [{}]",ex.getMessage(),ex);
            return "delete dail";
        }
    }

    /**
     * 更新路由
     * @param definition
     * @return
     */
    private String updateByRouteDefinition(RouteDefinition definition) {

        try {
            log.info("gateway update route : [{}]",definition);
            //删除
            routeDefinitionWriter.delete(Mono.just(definition.getId()));
        }catch (Exception ex) {
            log.error("update fail ,not find routeId : [{}]",definition.getId());
            return "update fail"+definition.getId();
        }

        try {
            log.info("gateway update route save : [{}]",definition);
            routeDefinitionWriter.save(Mono.just(definition)).subscribe();
            this.publisher.publishEvent(new RefreshRoutesEvent(this));
            return "success";
        } catch (Exception exception) {
            log.error("update fail : [{}]",definition.getId());
            return "update fail";
        }
    }

    /**
     * 更新路由公有方法
     * @param definitions
     * @return
     */
    public String updateList(List<RouteDefinition> definitions) {

        log.info("gateway update route : [{}]",definitions);

        //先拿到当前gateway中存储的路由定义
        List<RouteDefinition> routeDefinitionsExist = routeDefinitionLocator.getRouteDefinitions().buffer().blockFirst();
        if (!CollectionUtils.isEmpty(routeDefinitionsExist)) {
            //清除之前所有配置的旧配置
            routeDefinitionsExist.forEach(rd -> {
                log.info("delete route definition : [{}]",rd);
                deleteById(rd.getId());
            });
        }

        //把更新的路由定义同步到gateway中
        definitions.forEach(definition -> updateByRouteDefinition(definition));
        return "success";
    }
}

DynamicRouteServiceImpl类实现ApplicationEventPublisherAware接口用来之后发布config配置变更的消息,这个类的作用是实现对Gateway config的增加和修改

创建DynamicRouteServiceImplByNacos

/**
 * @author qingtian
 * @description:通过nacos下发动态路由配置,监听naocs中路由配置变更
 * @Package com.imooc.ecommerce.config
 * @date 2021/12/7 16:29
 */
@Component
@Slf4j
@DependsOn({"gatewayConfig"})
public class DynamicRouteServiceImplByNacos {

    /**
     * nacos配置服务
     */
    private ConfigService configService;

    private final DynamicRouteServiceImpl dynamicRouteService;

    public DynamicRouteServiceImplByNacos(DynamicRouteServiceImpl dynamicRouteService) {
        this.dynamicRouteService = dynamicRouteService;
    }

    /**
     *初始化nacos config
     * @return
     */
    private ConfigService initConfigService() {

        try {
            Properties properties = new Properties();
            properties.setProperty("serverAddr",GatewayConfig.NACOS_SERVER_ADDR);
            properties.setProperty("namespace",GatewayConfig.NACOS_NAMESPACE);
            return configService = NacosFactory.createConfigService(properties);
        } catch (Exception ex) {
            log.error("init gateway config error : [{}]",ex.getMessage(),ex);
            return null;
        }
    }

    /**
     * 监听nacos 下发的动态路由配置
     * @param dataId
     * @param group
     */
    private void dynamicRouteNacosListener(String dataId,String group) {

        try {

            //给Nacos Config客户端增加一个监听器
            configService.addListener(dataId, group, new Listener() {

                /**
                 * 自己提供线程池执行操作
                 * ,如果不提供则使用默认的线程池
                 * @return
                 */
                @Override
                public Executor getExecutor() {
                    return null;
                }

                /**
                 * 监听器收到配置更新
                 * @param s
                 */
                @Override
                public void receiveConfigInfo(String s) {

                    log.info("start to update config: [{}]",s);
                    List<RouteDefinition> definitionList = JSON.parseArray(s, RouteDefinition.class);
                    log.info("update route : [{}]",definitionList.toString());
                    //更新路由配置
                    dynamicRouteService.updateList(definitionList);
                }
            });
        }catch (NacosException ex) {
            log.error("dynamic update gateway config error : [{}],",ex.getMessage(),ex);
        }
    }

    /**
     * Bean在容器中构造完成之后会执行
     */
    @PostConstruct
    public void init() {

        log.info("gateway route init ...");

        try {
            //初始化Nacos配置客户端
            configService = initConfigService();
            if (null == configService) {
                log.error("init configService fail");
                return;
            }

            //通过Nacos config并指定路由配置去获取路由配置
            String configInfo = configService.getConfig(
                    GatewayConfig.NACOS_ROUTER_DATA_ID,
                    GatewayConfig.NACOS_ROUTER_GROUP,
                    GatewayConfig.DEFAULT_TIMEOUT
            );

            log.info("get current gateway config : [{}]",configInfo);
            List<RouteDefinition> definitionList = JSON.parseArray(configInfo, RouteDefinition.class);
            if (CollectionUtil.isNotEmpty(definitionList)) {
                for (RouteDefinition routeDefinition : definitionList) {
                    log.info("init gateway info : [{}]",routeDefinition.toString());
                    dynamicRouteService.addRouteDefinition(routeDefinition);
                }
            }
        } catch (Exception ex) {
            log.info("gateway config init has some error : [{}]",ex.getMessage(),ex);
        }
        //设置监听器
        dynamicRouteNacosListener(GatewayConfig.NACOS_ROUTER_DATA_ID,GatewayConfig.NACOS_ROUTER_GROUP);
    }
}

注意:使用了@DependsOn({"gatewayConfig"})注解,表明了此类的初始化要依赖于

gatewayConfig类,gatewayConfig需要的此类之前完成初始化,否则读取不到NACOS_SERVER_ADDR等属性。

再看到方法init()使用了注解@PostConstruct表明在此bean的实例化结束的第一时间执行init()方法,同时在init()方法中创建了dynamicRouteNacosListener用于监听Gateway configNacos中的更新

测试

  • 启动GatewayApplication

  • 查看日志:

    image-20211213153515192

    可以看到在log日志中成功打印出了nacos中的gateway配置

  • 验证gateway config配置监听自动更新功能

    • nacos中更改配置

      image-20211213154504349

    • 查看日志是否更新

      image-20211213154555770

    • 可以看出gateway成功监听到nacos中的更新并且在本地进行了修改

0

评论区