• Spring Cloud Zuul的动态路由怎样做?集成Nacos实现很简单


    一、说明

    网关的核心概念就是路由配置和路由规则,而作为所有请求流量的入口,在实际生产环境中为了保证高可靠和高可用,是尽量要避免重启的,所以实现动态路由是非常有必要的;本文主要介绍实现的思路,并且以Nacos为数据源来讲解

     

    二、实现要点

    要实现动态路由只需关注下面4个点

    1. 网关启动时,动态路由的数据怎样加载进来
    2. 静态路由动态路由以那个为准,ps:静态路由指的是配置文件里写死的路由配置
    3. 监听动态路由的数据源变化
    4. 数据有变化时怎样通知zuul刷新路由

     

    三、具体实现

    3.1. 实现动态路由的数据加载

    • 重写SimpleRouteLocator类的locateRoutes方法,此方法是加载路由配置的,父类中是获取properties中的路由配置,可以通过扩展此方法,达到动态获取配置的目的
    • 这里采用静态路由动态路由共存,相同路由id以动态路由优先覆盖的实现方式

    AbstractDynRouteLocator类可查看:AbstractDynRouteLocator.java

    public abstract class AbstractDynRouteLocator extends SimpleRouteLocator implements RefreshableRouteLocator {
        private ZuulProperties properties;
    
        public AbstractDynRouteLocator(String servletPath, ZuulProperties properties) {
            super(servletPath, properties);
            this.properties = properties;
        }
    
        /**
         * 刷新路由
         */
        @Override
        public void refresh() {
            doRefresh();
        }
    
        @Override
        protected Map<String, ZuulRoute> locateRoutes() {
            LinkedHashMap<String, ZuulRoute> routesMap = new LinkedHashMap<>();
            // 从application.properties中加载静态路由信息
            routesMap.putAll(super.locateRoutes());
            // 从数据源中加载动态路由信息
            routesMap.putAll(loadDynamicRoute());
            // 优化一下配置
            LinkedHashMap<String, ZuulRoute> values = new LinkedHashMap<>();
            for (Map.Entry<String, ZuulRoute> entry : routesMap.entrySet()) {
                String path = entry.getKey();
                // Prepend with slash if not already present.
                if (!path.startsWith("/")) {
                    path = "/" + path;
                }
                if (StringUtils.hasText(this.properties.getPrefix())) {
                    path = this.properties.getPrefix() + path;
                    if (!path.startsWith("/")) {
                        path = "/" + path;
                    }
                }
                values.put(path, entry.getValue());
            }
            return values;
        }
    
        /**
         * 加载路由配置,由子类去实现
         */
        public abstract Map<String, ZuulRoute> loadDynamicRoute();
    }
    

    由于动态路由的数据可以有很多种途径,如:Nacos、Redis、Zookeeper、DB等,所以这里定义一个抽象类,由具体的实现类去定义loadDynamicRoute方法

     

    3.2. Nacos路由实现类

    NacosDynRouteLocator类完整的代码实现可查看:NacosDynRouteLocator.java

    3.2.1. 实现loadDynamicRoute方法获取动态数据

        @Override
        public Map<String, ZuulProperties.ZuulRoute> loadDynamicRoute() {
            Map<String, ZuulRoute> routes = new LinkedHashMap<>();
            if (zuulRouteEntities == null) {
                zuulRouteEntities = getNacosConfig();
            }
            for (ZuulRouteEntity result : zuulRouteEntities) {
                if (StrUtil.isBlank(result.getPath()) || !result.isEnabled()) {
                    continue;
                }
                ZuulRoute zuulRoute = new ZuulRoute();
                BeanUtil.copyProperties(result, zuulRoute);
                routes.put(zuulRoute.getPath(), zuulRoute);
            }
            return routes;
        }
    		
        private List<ZuulRouteEntity> getNacosConfig() {
            try {
                String content = nacosConfigProperties.configServiceInstance().getConfig(ZUUL_DATA_ID, ZUUL_GROUP_ID,5000);
                return getListByStr(content);
            } catch (NacosException e) {
                log.error("listenerNacos-error", e);
            }
            return new ArrayList<>(0);
        }
    

    3.2.2. 增加NacosListener监听路由数据变化

        private void addListener() {
            try {
                nacosConfigProperties.configServiceInstance().addListener(ZUUL_DATA_ID, ZUUL_GROUP_ID, new Listener() {
                    @Override
                    public Executor getExecutor() {
                        return null;
                    }
    
                    @Override
                    public void receiveConfigInfo(String configInfo) {
                        //赋值路由信息
                        locator.setZuulRouteEntities(getListByStr(configInfo));
                        RoutesRefreshedEvent routesRefreshedEvent = new RoutesRefreshedEvent(locator);
                        publisher.publishEvent(routesRefreshedEvent);
                    }
                });
            } catch (NacosException e) {
                log.error("nacos-addListener-error", e);
            }
        }
    

    注意路由数据变化后不需要自己手动刷新路由,只需要给zuul发送一个RoutesRefreshedEvent事件即可,zuul自己有个ZuulRefreshListener类会监听事件帮我们刷新路由

     

    3.3. 配置类创建NacosDynRouteLocator的Bean

    DynamicZuulRouteConfig可查看:NacosDynRouteLocator.java

    @Configuration
    @ConditionalOnProperty(prefix = "zlt.gateway.dynamicRoute", name = "enabled", havingValue = "true")
    public class DynamicZuulRouteConfig {
        @Autowired
        private ZuulProperties zuulProperties;
    
        @Autowired
        private DispatcherServletPath dispatcherServletPath;
    
        /**
         * Nacos实现方式
         */
        @Configuration
        @ConditionalOnProperty(prefix = "zlt.gateway.dynamicRoute", name = "dataType", havingValue = "nacos", matchIfMissing = true)
        public class NacosZuulRoute {
            @Autowired
            private NacosConfigProperties nacosConfigProperties;
    
            @Autowired
            private ApplicationEventPublisher publisher;
    
            @Bean
            public NacosDynRouteLocator nacosDynRouteLocator() {
                return new NacosDynRouteLocator(nacosConfigProperties, publisher, dispatcherServletPath.getPrefix(), zuulProperties);
            }
        }
    }
    

    这里通过自定义配置来控制是否开启动态路由功能

     

    3.4. 添加Nacos路由配置

    file
    新增配置项:

    • Data Id:zuul-routes
    • Group:ZUUL_GATEWAY
    • 配置内容:
    [
        {
            "enabled":true,
            "id":"csdn",
            "path":"/csdn/**",
            "retryable":false,
            "stripPrefix":true,
            "url":"https://www.csdn.net/"
        }, {
            "enabled":true,
            "id":"github",
            "path":"/github/**",
            "retryable":false,
            "stripPrefix":true,
            "url":"http://github.com/"
        }
    ]
    

    添加两条路由数据

     

    四、测试

    • 启动网关通过/actuator/routes端点查看当前路由信息
      file

    可以看到静态路由和Nacos里配置的两条路由信息并存显示

    • 修改Nacos配置,关闭csdn路由
      file
    • 刷新查看网关的路由信息
      file

    csdn的路由已经看不到了,实现了动态改变路由配置

     
    推荐阅读

     
    请扫码关注我的公众号
    file

  • 相关阅读:
    avalon ms-repeat avalon1
    抓取各个浏览器引擎关键字,,百度学术关键字
    JS中Float类型加减乘除 修复
    js jquery 权限单选 bug修改以及正确代码 购物车数量加减
    图片加载错误默认显示图片
    文本框中焦点竖杆长度控制
    Go语言中 Print,Println 和 Printf 的区别(八)
    typescript中新增的基本数据类型
    尝试自己搭一个简单的typescript运行环境
    Go语言流程控制中的break,continue和goto(七)
  • 原文地址:https://www.cnblogs.com/zlt2000/p/11428272.html
Copyright © 2020-2023  润新知