zoukankan      html  css  js  c++  java
  • 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

  • 相关阅读:
    Linux IO接口 监控 (iostat)
    linux 防火墙 命令
    _CommandPtr 添加参数 0xC0000005: Access violation writing location 0xcccccccc 错误
    Visual Studio自动关闭
    Linux vsftpd 安装 配置
    linux 挂载外部存储设备 (mount)
    myeclipse 9.0 激活 for win7 redhat mac 亲测
    英文操作系统 Myeclipse Console 乱码问题
    Linux 基本操作命令
    linux 查看系统相关 命令
  • 原文地址:https://www.cnblogs.com/zlt2000/p/11428272.html
Copyright © 2011-2022 走看看