最近做一个新项目,用到了Gateway

考虑到之后开发环境的的路由和线上的必定不一致,而且后续可能会涉及到修正和新增路由信息

所以,动态路由是必须要上的,废话不多说,开车!

Spring Cloud Gateway 实现动态路由(超简单版)

装置服务中心(注册中心)Nacos

想要完成Gateway动态路由,服务中心是前置条件,在国内,现在最主流的服务中心,应该非Nacos莫属了

没装置的小伙伴,参阅下我的这篇文章,装置过程十分简单

现已装置好了的,咱们持续往下看

pom 依靠

        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-gateway</artifactId>
            <version>3.1.7</version>
        </dependency>
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
            <version>2021.0.5.0</version>
        </dependency>
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
            <version>2021.0.5.0</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-loadbalancer</artifactId>
            <version>3.1.6</version>
        </dependency>

yml 装备文件修正

修正前

server:
  port: 8080
spring:
  application:
    name: my-gateway
  cloud:
    nacos:
      # IP端口和用户名、暗码
      server-addr: 127.0.0.1:8868
      username: nacos
      password: nacos
    gateway:
      routes:
        - id: api
          # 匹配后,转发到此域名
          uri: lb://api
          # 匹配途径
          predicates:
            - Path=/api/**
          # 过滤器装备
          filters:
            - StripPrefix=1

修正后

但等会儿咱们完成了动态 routes 之后,.yml 文件里 routes就没有用了,咱们可以把它注释或许删除 大家自行参阅下方装备进行修正,新增的和注释的行,我都做有符号。

server:
  port: 8080
spring:
  application:
    name: my-gateway
  cloud:
    nacos:
      # IP端口和用户名、暗码
      server-addr: 127.0.0.1:8868
      username: nacos
      password: nacos
+      config:
+        # 禁用Spring的装备导入检查
+        import-check:
+         enabled: false
    gateway:
+      discovery:
+        locator:
+        	# 开启通过服务中心动态更新装备的功用
+          enabled: true
#      routes:
#        - id: api
#          # 匹配后,转发到此域名
#          uri: lb://api
#          # 匹配途径
#          predicates:
#            - Path=/api/**
#          # 过滤器装备
#          filters:
#            - StripPrefix=1

监听变更与更新路由装备

咱们需要写一点Java代码,监听Nacos上面的装备变更,然后更新路由信息


    /**
     * 路由信息变更监听
     */
    @Component
    @RefreshScope
    public class OnRouteConfigChange implements ApplicationEventPublisherAware {
        private static final Logger LOGGER = LoggerFactory.getLogger(OnRouteConfigChange.class);
        /** 路由信息写入器 */
        @Resource
        private RouteDefinitionWriter routeDefinitionWriter;
        /** 获取yml文件里nacos的装备信息 */
        @Resource
        private NacosConfigProperties nacosConfigProperties;
        /** nacos 装备服务 */
        private ConfigService configService;
        /** 应用事情发布器 */
        private ApplicationEventPublisher publisher;
        /**
         * 获取 nacos 装备服务
         * @return
         * @throws NacosException
         */
        private ConfigService getConfigService() throws NacosException {
            // 懒汉加载,用到再创立这个服务
            if (null == configService){
                Properties properties = new Properties();
                properties.setProperty(PropertyKeyConst.SERVER_ADDR, nacosConfigProperties.getServerAddr());
                properties.setProperty(PropertyKeyConst.USERNAME, nacosConfigProperties.getUsername());
                properties.setProperty(PropertyKeyConst.PASSWORD, nacosConfigProperties.getPassword());
                configService = NacosFactory.createConfigService(properties);
            }
            return configService;
        }
        /**
         *  网关初始化
         */
        @PostConstruct
        public void initGatewayRoutes() {
            try {
                // 装备ID
                String dateId = "my_gateway_route_config";
                // 分组
                String group = "MY_GATEWAY_ROUTE";
                // 获取当时Nacos里,对应dataId的装备数据,随后开始监听对应dataId数据变更
                String configJson = getConfigService().getConfigAndSignListener(dateId, group, nacosConfigProperties.getTimeout(), new Listener() {
                    @Override
                    public Executor getExecutor() { return null; }
                    /**
                     * 数据变更时触发
                     * @param configInfo 新的装备信息
                     */
                    @Override
                    public void receiveConfigInfo(String configInfo) {
                        LoggerUtils.info(LOGGER, "监听到网关路由装备变更:{0}", configInfo);
                        saveRoutesConfig(configInfo);
                    }
                });
                LoggerUtils.info(LOGGER, "初始化网关路由装备:{0}", configJson);
                saveRoutesConfig(configJson);
            } catch (Exception e) {
                LoggerUtils.error(e, LOGGER, "初始化网关路由时产生过错", e);
            }
        }
        /**
         * 保存路由装备
         * @param configJson
         */
        private void saveRoutesConfig(String configJson){
            List<RouteDefinition> definitions = JSONArray.parseArray(configJson, RouteDefinition.class);
            if (!CollectionUtils.isEmpty(definitions)){
                // 遍历更新路由信息
                for (RouteDefinition definition : definitions){
                    routeDefinitionWriter.save(Mono.just(definition)).subscribe();
                }
                // 发送改写路由的事情告诉
                publisher.publishEvent(new RefreshRoutesEvent(this));
            }
        }
        @Override
        public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
            publisher = applicationEventPublisher;
        }
    }

在 Nacos 里装备路由信息

翻开Nacos的控制台(我的Nacos端口装备成了8868,大家自行根据自己的端口装备来访问)

http://127.0.0.1:8868/nacos/

点击【创立装备】

Spring Cloud Gateway 实现动态路由(超简单版)

如下图进行装备即可

装备内容信息,现已贴在下方了,大家可以自行复制参阅

Spring Cloud Gateway 实现动态路由(超简单版)

Data ID 和 Group 其实便是上面Java代码中的dateIdgroup,这里一定要对上,不然拉取不到装备

    // 装备ID
    String dateId = "my_gateway_route_config";
    // 分组
    String group = "MY_GATEWAY_ROUTE";

装备内容(JSON)

    [
        {
            "id": "api",
            "uri": "lb://api",
            "predicates": [
                {
                    "args": {
                        "pattern": "/api/**",
                    },
                    "name": "Path"
                }
            ],
            "filters": [
                {
                    "args": {
                        "parts": 1
                    },
                    "name": "StripPrefix"
                }
            ],
            "order": 1
        }
    ]

拓展知识:

1、装备内容是根据 org.springframework.cloud.gateway.route.RouteDefinition 的字段内容

2、args 里的内容,key名称可以随意设置,只要和这个args里的其他字段不重名就可以,它便是一个Map

例如 "pattern": "/api/**" 设置成 "pattern1": "/api/**",作用是相同的

3、args 里,假如想设置多个条件,加字段即可

例如 "args":{"pattern1": "/api/**", "pattern2": "/apis/**"}

验证是否成功

为了可以比较直观的验证,咱们从头【编辑】一下Nacos上面的装备内容

uri改成 https://aliyun.com/,点击【发布】

然后,运转项目

在浏览器中输入:http://localhost:8080/api

Spring Cloud Gateway 实现动态路由(超简单版)

成功进入了 aliyun.com 阿里云的页面,说明动态装备收效了

再试试改成 baidu.com ,和上面的过程相同,编辑->修正uri->发布

然后改写页面,和预期的相同,进入了百度的页面

Spring Cloud Gateway 实现动态路由(超简单版)

搞定!

假如对您有帮助,请点个赞吧。

最终弥补一个点

偶尔出现 gateway Failed to resolve 'xxx.com' after 5 queries 过错,过一会或许重启就好了,原因不清。