• springcloud3(五) spring cloud gateway动态路由的四类实现方式


    写这篇博客主要是为了汇总下动态路由的多种实现方式,没有好坏之分,任何的方案都是依赖业务场景需求的,现在网上实现方式主要有: 基于Nacos, 基于数据库(PosgreSQL/Redis), 基于Memory(内存),而我们公司是第四种方案:基于File(本地文件),通过不同文件来隔离不同业务线的路由,大佬们不要喷,任何方案脱离不了业务场景(各种难言之隐)。下面主要简单介绍下这四种动态路由的实现方式

    1.基于Nacos的动态路由

    Nacos官方简介

    Nacos 致力于帮助您发现、配置和管理微服务。Nacos 提供了一组简单易用的特性集,帮助您快速实现动态服务发现、服务配置、服务元数据及流量管理。Nacos 帮助您更敏捷和容易地构建、交付和管理微服务平台。 Nacos 是构建以“服务”为中心的现代应用架构 (例如微服务范式、云原生范式) 的服务基础设施。主要特性如下:

    1. 服务发现和服务健康监测
    2. 动态配置服务
    3. 动态 DNS 服务
    4. 服务及其元数据管理

    此处不展开介绍Nacos了,主要讲下Spring Cloud Gateway + Nacos 实现动态路由

    1.1 相关版本如下

    spring-cloud-starter-gateway:2.1.0.RELEASE
    spring-cloud-starter-alibaba-nacos-config:2.2.5.RELEASE

    1.2 实现思路

     ok,上代码

    properties配置

    ### nacos configuration start
    spring.cloud.nacos.discovery.server-addr=127.0.0.1:8848
    spring.cloud.nacos.discovery.namespace=50f5dcf0-f3c0-4c79-9715-0e25e3959ssd
    nacos.gateway.route.config.data-id=server-routes
    nacos.gateway.route.config.group=spb-gateway
    ### nacos configuration end

    NacosGatewayConfig配置类

    package com.kawa.spbgateway.config;
    
    import com.fasterxml.jackson.databind.ObjectMapper;
    import com.fasterxml.jackson.databind.SerializationFeature;
    import org.springframework.beans.factory.annotation.Value;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    
    @Configuration
    public class NacosGatewayConfig {
        public static final long DEFAULT_TIMEOUT = 30000;
    
        public static String NACOS_SERVER_ADDR;
    
        public static String NACOS_NAMESPACE;
    
        public static String NACOS_ROUTE_DATA_ID;
    
        public static String NACOS_ROUTE_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 setNacosRouteDataId(String nacosRouteDataId) {
            NACOS_ROUTE_DATA_ID = nacosRouteDataId;
        }
    
        @Value("${nacos.gateway.route.config.group}")
        public void setNacosRouteGroup(String nacosRouteGroup) {
            NACOS_ROUTE_GROUP = nacosRouteGroup;
        }
    
        @Bean
        public ObjectMapper getObjectMapper() {
            ObjectMapper objectMapper = new ObjectMapper();
            objectMapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
            return objectMapper;
        }
    }

    NacosDynamicRouteService类

    加载和监听路由

    package com.kawa.spbgateway.service;
    
    import com.alibaba.nacos.api.NacosFactory;
    import com.alibaba.nacos.api.config.ConfigService;
    import com.alibaba.nacos.api.config.listener.Listener;
    import com.alibaba.nacos.api.exception.NacosException;
    import com.fasterxml.jackson.core.JsonProcessingException;
    import com.fasterxml.jackson.core.type.TypeReference;
    import com.fasterxml.jackson.databind.ObjectMapper;
    import com.kawa.spbgateway.route.CustomizedRouteDefinition;
    import lombok.extern.slf4j.Slf4j;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.cloud.gateway.route.RouteDefinition;
    import org.springframework.context.annotation.DependsOn;
    import org.springframework.stereotype.Service;
    
    import javax.annotation.PostConstruct;
    import java.util.List;
    import java.util.Properties;
    import java.util.concurrent.Executor;
    
    import static com.kawa.spbgateway.config.NacosGatewayConfig.*;
    
    @Service
    @Slf4j
    @DependsOn({"nacosGatewayConfig"})
    public class NacosDynamicRouteService {
    
        @Autowired
        private NacosRefreshRouteService nacosRefreshRouteService;
    
        private ConfigService configService;
    
        @Autowired
        private ObjectMapper objectMapper;
    
        @PostConstruct
        public void init() {
            log.info(">>>>>>>>>> init gateway route <<<<<<<<<<");
            configService = initConfigService();
            if (null == configService) {
                log.error(">>>>>>> init the ConfigService failed!!!");
            }
            String configInfo = null;
            try {
                configInfo = configService.getConfig(NACOS_ROUTE_DATA_ID, NACOS_ROUTE_GROUP, DEFAULT_TIMEOUT);
                log.info(">>>>>>>>> get the gateway configInfo:
    {}", configInfo);
                List<CustomizedRouteDefinition> routeDefinitions = objectMapper.readValue(configInfo, new TypeReference<List<CustomizedRouteDefinition>>() {
                });
    
                for (RouteDefinition definition : routeDefinitions) {
                    log.info(">>>>>>>>>> load route : {}", definition.toString());
                    nacosRefreshRouteService.add(definition);
                }
            } catch (NacosException | JsonProcessingException e) {
                e.printStackTrace();
            }
            dynamicRouteByNacosListener(NACOS_ROUTE_DATA_ID, NACOS_ROUTE_GROUP);
        }
    
        private void dynamicRouteByNacosListener(String dataId, String group) {
            try {
                configService.addListener(dataId, group, new Listener() {
                    @Override
                    public Executor getExecutor() {
                        log.info("-------------------getExecutor-------------------");
                        return null;
                    }
    
                    @Override
                    public void receiveConfigInfo(String configInfo) {
                        log.info(">>>>>>>>> listened configInfo change:
    	{}", configInfo);
                        List<CustomizedRouteDefinition> routeDefinitions = null;
                        try {
                            routeDefinitions = objectMapper.readValue(configInfo, new TypeReference<>() {
                            });
                        } catch (JsonProcessingException e) {
                            e.printStackTrace();
                        }
                        nacosRefreshRouteService.updateList(routeDefinitions);
                    }
                });
            } catch (NacosException e) {
                e.printStackTrace();
            }
        }
    
        private ConfigService initConfigService() {
            Properties properties = new Properties();
            properties.setProperty("serverAddr", NACOS_SERVER_ADDR);
            properties.setProperty("namespace", NACOS_NAMESPACE);
            try {
                return NacosFactory.createConfigService(properties);
            } catch (NacosException e) {
                e.printStackTrace();
                return null;
            }
        }
    
    }

    NacosRefreshRouteService类 

    实现路由的更新和刷新本地缓存

    package com.kawa.spbgateway.service;
    
    import com.kawa.spbgateway.route.CustomizedRouteDefinition;
    import lombok.extern.slf4j.Slf4j;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.cloud.gateway.event.RefreshRoutesEvent;
    import org.springframework.cloud.gateway.route.RouteDefinition;
    import org.springframework.cloud.gateway.route.RouteDefinitionLocator;
    import org.springframework.cloud.gateway.route.RouteDefinitionWriter;
    import org.springframework.context.ApplicationEventPublisher;
    import org.springframework.context.ApplicationEventPublisherAware;
    import org.springframework.stereotype.Service;
    import org.springframework.util.CollectionUtils;
    import reactor.core.publisher.Mono;
    
    import java.util.ArrayList;
    import java.util.List;
    
    
    @Service
    @Slf4j
    public class NacosRefreshRouteService implements ApplicationEventPublisherAware {
    
        private ApplicationEventPublisher publisher;
    
        @Autowired
        private RouteDefinitionWriter routeDefinitionWriter;
    
        @Autowired
        private RouteDefinitionLocator routeDefinitionLocator;
    
        private List<String> routeIds = new ArrayList<>();
    
        @Override
        public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
            this.publisher = applicationEventPublisher;
        }
    
        /**
         * 删除路由
         *
         * @param id
         * @return
         */
        public void delete(String id) {
            try {
                log.info(">>>>>>>>>> gateway delete route id {}", id);
                this.routeDefinitionWriter.delete(Mono.just(id)).subscribe();
                this.publisher.publishEvent(new RefreshRoutesEvent(this));
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    
        /**
         * 更新路由
         *
         * @param definitions
         * @return
         */
        public void updateList(List<CustomizedRouteDefinition> definitions) {
            log.info(">>>>>>>>>> gateway update route {}", definitions);
            // 删除缓存routerDefinition
            List<RouteDefinition> routeDefinitionsExits = routeDefinitionLocator.getRouteDefinitions().buffer().blockFirst();
            if (!CollectionUtils.isEmpty(routeDefinitionsExits)) {
                routeDefinitionsExits.forEach(routeDefinition -> {
                    log.info("delete routeDefinition:{}", routeDefinition);
                    delete(routeDefinition.getId());
                });
            }
            definitions.forEach(definition -> {
                updateById(definition);
            });
        }
    
        /**
         * 更新路由
         *
         * @param definition
         * @return
         */
        public void updateById(RouteDefinition definition) {
            try {
                log.info(">>>>>>>>>> gateway update route {}", definition);
                this.routeDefinitionWriter.delete(Mono.just(definition.getId()));
            } catch (Exception e) {
                e.printStackTrace();
            }
            try {
                routeDefinitionWriter.save(Mono.just(definition)).subscribe();
                this.publisher.publishEvent(new RefreshRoutesEvent(this));
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    
        /**
         * 增加路由
         *
         * @param definition
         * @return
         */
        public void add(RouteDefinition definition) {
            log.info(">>>>>>>>>> gateway add route {}", definition);
            routeDefinitionWriter.save(Mono.just(definition)).subscribe();
            this.publisher.publishEvent(new RefreshRoutesEvent(this));
        }
    
    }

    测试一下

    nacos添加路由配置,注意"Data ID" 和 “Group”要和配置一一对应

    启动项目加载配置,可以看到加载路由配置的日志(监听路由变化的日志就不截图了)

    也可以通过actuator的接口测试下(可以看到已经路由已经加载到本地内存)

     

    2. 基于数据库(PosgreSQL/Redis)的动态路由

    基于数据库,关系型数据库和非关系型数据库,实现思路是一样的,这里我就以Redis来举例子

    2.1 相关配置

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-redis</artifactId>
    </dependency>

    2.2  实现思路

     

    上代码

    proerties配置

    ### redis configuration start
    spring.redis.database=0
    spring.redis.host=127.0.0.1
    spring.redis.port=10619
    spring.redis.password=asdqwe
    ### redis configuratiokn end

    RedisConfiguration类

    package com.kawa.spbgateway.config;
    
    import com.fasterxml.jackson.annotation.JsonAutoDetect;
    import com.fasterxml.jackson.annotation.PropertyAccessor;
    import com.fasterxml.jackson.databind.ObjectMapper;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.data.redis.connection.RedisConnectionFactory;
    import org.springframework.data.redis.core.StringRedisTemplate;
    import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
    import org.springframework.data.redis.serializer.StringRedisSerializer;
    
    @Configuration
    public class RedisConfiguration {
        @Bean
        public StringRedisTemplate redisTemplate(RedisConnectionFactory redisConnectionFactory) {
            StringRedisTemplate redisTemplate = new StringRedisTemplate();
            //设置工厂链接
            redisTemplate.setConnectionFactory(redisConnectionFactory);
            //设置自定义序列化方式
            setSerializeConfig(redisTemplate);
            return redisTemplate;
        }
    
        private void setSerializeConfig(StringRedisTemplate redisTemplate) {
            StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
            redisTemplate.setKeySerializer(stringRedisSerializer);
            redisTemplate.setHashKeySerializer(stringRedisSerializer);
            Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
            ObjectMapper om = new ObjectMapper();
            om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
            jackson2JsonRedisSerializer.setObjectMapper(om);
            redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);
            redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer);
            redisTemplate.afterPropertiesSet();
        }
    }

    RedisDynamicRouteService类

    操作redis的类

    package com.kawa.spbgateway.service;
    
    import com.fasterxml.jackson.core.JsonProcessingException;
    import com.fasterxml.jackson.databind.ObjectMapper;
    import lombok.extern.slf4j.Slf4j;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.cloud.gateway.route.RouteDefinition;
    import org.springframework.cloud.gateway.support.NotFoundException;
    import org.springframework.data.redis.core.StringRedisTemplate;
    import org.springframework.stereotype.Service;
    import reactor.core.publisher.Flux;
    import reactor.core.publisher.Mono;
    
    import java.util.ArrayList;
    import java.util.List;
    
    @Slf4j
    @Service
    public class RedisDynamicRouteService {
    
        public static final String GATEWAY_ROUTES_PREFIX = "brian:sz_home:gateway_dynamic_route:";
    
        @Autowired
        private StringRedisTemplate redisTemplate;
    
        @Autowired
        private ObjectMapper objectMapper;
    
        public Flux<RouteDefinition> getRouteDefinitions() {
            log.info(">>>>>>>>>> getRouteDefinitions <<<<<<<<<<");
            List<RouteDefinition> routeDefinitions = new ArrayList<>();
            redisTemplate.keys(GATEWAY_ROUTES_PREFIX+"*").stream().forEach(key -> {
                String rdStr = redisTemplate.opsForValue().get(key);
                RouteDefinition routeDefinition = null;
                try {
                    routeDefinition = objectMapper.readValue(rdStr, RouteDefinition.class);
                    routeDefinitions.add(routeDefinition);
                } catch (JsonProcessingException e) {
                    e.printStackTrace();
                }
    
            });
            return Flux.fromIterable(routeDefinitions);
        }
    
        public Mono<Void> save(Mono<RouteDefinition> route) {
            return route.flatMap(routeDefinition -> {
                String rdStr = null;
                try {
                    rdStr = objectMapper.writeValueAsString(routeDefinition);
                    redisTemplate.opsForValue().set(GATEWAY_ROUTES_PREFIX + routeDefinition.getId(), rdStr);
                } catch (JsonProcessingException e) {
                    e.printStackTrace();
                }
    
                return Mono.empty();
            });
        }
    
        public Mono<Void> delete(Mono<String> routeId) {
            return routeId.flatMap(id -> {
                if (redisTemplate.hasKey(GATEWAY_ROUTES_PREFIX + id)) {
                    redisTemplate.delete(GATEWAY_ROUTES_PREFIX + id);
                    return Mono.empty();
                }
                return Mono.defer(() -> Mono.error(new NotFoundException("routeDefinition not found, id is: " + id)));
            });
        }
    
        public Mono<Boolean> get(Mono<String> routeId) {
            return routeId.flatMap(id -> Mono.just(redisTemplate.hasKey(GATEWAY_ROUTES_PREFIX + id)));
        }
    }

    RedisRefreshRouteService类

    动态刷新路由

    package com.kawa.spbgateway.service;
    
    import lombok.extern.slf4j.Slf4j;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.boot.ApplicationArguments;
    import org.springframework.boot.ApplicationRunner;
    import org.springframework.cloud.gateway.event.RefreshRoutesEvent;
    import org.springframework.cloud.gateway.route.RouteDefinition;
    import org.springframework.cloud.gateway.route.RouteDefinitionWriter;
    import org.springframework.context.ApplicationEventPublisher;
    import org.springframework.context.ApplicationEventPublisherAware;
    import org.springframework.stereotype.Service;
    import org.springframework.util.Assert;
    import reactor.core.publisher.Flux;
    import reactor.core.publisher.Mono;
    
    
    @Slf4j
    @Service
    public class RedisRefreshRouteService implements ApplicationEventPublisherAware, ApplicationRunner {
    
        @Autowired
        private RedisDynamicRouteService repository;
    
        @Autowired
        private RouteDefinitionWriter routeDefinitionWriter;
    
    
        private ApplicationEventPublisher publisher;
    
        @Override
        public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
            this.publisher = applicationEventPublisher;
        }
    
        private void loadRoutes(){
            log.info(">>>>>>>>>> init routes from redis <<<<<<<<<<");
            Flux<RouteDefinition> routeDefinitions = repository.getRouteDefinitions();
            routeDefinitions.subscribe(r-> {
                routeDefinitionWriter.save(Mono.just(r)).subscribe();
            });
            publisher.publishEvent(new RefreshRoutesEvent(this));
        }
    
        public void add(RouteDefinition routeDefinition){
            Assert.notNull(routeDefinition.getId(),"routeDefinition is can not be null");
            repository.save(Mono.just(routeDefinition)).subscribe();
            routeDefinitionWriter.save(Mono.just(routeDefinition)).subscribe();
            publisher.publishEvent(new RefreshRoutesEvent(this));
        }
    
        public void update(RouteDefinition routeDefinition){
            Assert.notNull(routeDefinition.getId(),"routeDefinition is can not be null");
            repository.delete(Mono.just(routeDefinition.getId())).subscribe();
            routeDefinitionWriter.delete(Mono.just(routeDefinition.getId())).subscribe();
            repository.save(Mono.just(routeDefinition)).subscribe();
            routeDefinitionWriter.save(Mono.just(routeDefinition)).subscribe();
            publisher.publishEvent(new RefreshRoutesEvent(this));
        }
    
    
        public void delete(String id){
            Assert.notNull(id,"routeDefinition is can not be null");
            repository.delete(Mono.just(id)).subscribe();
            routeDefinitionWriter.delete(Mono.just(id)).subscribe();
            publisher.publishEvent(new RefreshRoutesEvent(this));
        }
    
        @Override
        public void run(ApplicationArguments args) throws Exception {
            loadRoutes();
        }
    }

    RedisDynamicRouteController类

    package com.kawa.spbgateway.controller;
    
    import com.kawa.spbgateway.service.RedisRefreshRouteService;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.cloud.gateway.route.RouteDefinition;
    import org.springframework.http.HttpStatus;
    import org.springframework.http.ResponseEntity;
    import org.springframework.web.bind.annotation.*;
    import reactor.core.publisher.Mono;
    
    @RestController
    @RequestMapping("/local")
    public class RedisDynamicRouteController {
    
        @Autowired
        private RedisRefreshRouteService dynamicRouteService;
    
        @PostMapping("/add")
        public Mono<ResponseEntity<String>> create(@RequestBody RouteDefinition entity) {
            dynamicRouteService.add(entity);
            return Mono.just(new ResponseEntity<>("save success", HttpStatus.OK));
        }
    
        @PostMapping("/update")
        public Mono<ResponseEntity<String>> update(@RequestBody RouteDefinition entity) {
            dynamicRouteService.update(entity);
            return Mono.just(new ResponseEntity<>("update success", HttpStatus.OK));
        }
    
        @PostMapping("/delete/{id}")
        public Mono<ResponseEntity<String>> delete(@PathVariable String id) {
            dynamicRouteService.delete(id);
            return Mono.just(new ResponseEntity<>("delete success", HttpStatus.OK));
        }
    
    }

    ok,测试下

    启动项目,查询下actuator接口,http://localhost:8080/actuator/gateway/routedefinitions 没有任何RouteDefinition

    postman插入一条RouteDefinition信息,http://127.0.0.1:8080/local/add

    再次查询RouteDefinitions信息,可以看到新添加进来的路由

    ok,测试下路由是否生效

    可以看到接口有数据返回,日志信息发现通过接口添加的路由生效了,转发到了目标接口

    接下来删除路由继续测试下

    调用删除接口后,通过actuator查询确认路由被删除了

    再次测试目标接口,404 Not Found

    3. 基于本地内存Memory的动态路由

    基于本地内存的方式比较简单,Spring Boot已经提供了两个组件Spring Boot Admin 和 Spring Boot Actuator,我这边只用Actuator来实现路由动态变化

    3.1 相关配置和接口

    <dependency>
          <groupId>org.springframework.boot</groupId>
         <artifactId>spring-boot-starter-actuator</artifactId>
    </dependency>


    o.s.c.g.a.GatewayControllerEndpoint:
    {GET /routes/{id}}: route(String)
    {GET /routes}: routes()
    {GET /routedefinitions}: routesdef()
    {GET /globalfilters}: globalfilters()
    {GET /routefilters}: routefilers()
    {GET /routepredicates}: routepredicates()
    {GET /routes/{id}/combinedfilters}: combinedfilters(String)
    {DELETE /routes/{id}}: delete(String)
    {POST /routes/{id}}: save(String,RouteDefinition)
    {POST /refresh}: refresh()

    3.2 实现思路

    和上面一样核心接口,routeDefinitionWriter.save(), routeDefinitionWriter.delete(),publisher.publishEvent(new RefreshRoutesEvent(this))

    测试一下

    项目启动的时候,不配置任何路由, 测试接口http://127.0.0.1:8080/actuator/gateway/routedefinitions 没有任何信息

    尝试添加一条路由信息,http://127.0.0.1:8080/actuator/gateway/routes/org.springframework.util.AlternativeJdkIdGenerator@3f203441

    最后测试下,路由有没有添加到内存,先刷新缓存http://127.0.0.1:8080/actuator/gateway/refresh,再次请求http://127.0.0.1:8080/actuator/gateway/routedefinitions

     

    可以发现路由已经到本地内存了,目标路由这里就不测试了,下面的基于File的动态路由会再次测试目标路由

    4.基于本地File的动态路由

    4.1 实现思路

    上代码

    route配置yml

    根据不用业务通过文件名区分开

    card-hk.yml

    routes:
      - uri: http://card-hk.${gateway.route.domain.postfix}
        predicates:
          - Path=/api/hk/card/v1/uuu/query
          - Method=POST
      - uri: http://card-hk.${gateway.route.domain.postfix}
        predicates:
          - Path=/api/hk/card/v1/er/query
          - Method=POST

    pancake.yml

    routes:
      - uri: http://pancake.${gateway.route.domain.postfix}
        predicates:
          - Path=^/api/pancake/v1/*,^/api/pancake/v1/*/query
    
      - predicates:
          - Path=/api/pancake/v1/coin/query
        filters:
          - RewritePath=/api/pancake/v1/coin/query, /api/v1/coin/query

    passport-hk.yml

    routes:
      - uri: http://passport-hk.${gateway.route.domain.postfix}
        predicates:
          - Path=/api/passport-hk/v1/passport/query
        auths:
          - sms

    FileRefreshRouteService类

    实现定时任务,启动刷新路由

    package com.kawa.spbgateway.service;
    
    import lombok.extern.slf4j.Slf4j;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.boot.CommandLineRunner;
    import org.springframework.cloud.gateway.event.RefreshRoutesEvent;
    import org.springframework.context.ApplicationEventPublisher;
    import org.springframework.context.ApplicationEventPublisherAware;
    import org.springframework.scheduling.annotation.Scheduled;
    import org.springframework.stereotype.Service;
    
    import java.io.IOException;
    
    @Slf4j
    @Service
    public class FileRefreshRouteService implements ApplicationEventPublisherAware, CommandLineRunner {
    
        @Autowired
        private FileDynamicRouteService routeService;
    
        @Autowired
        private ApplicationEventPublisher applicationEventPublisher;
    
        @Override
        public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
            this.applicationEventPublisher = applicationEventPublisher;
        }
    
        @Scheduled(cron = "0/5 * * * * ?")
        private void autoRefresh() {
            refreshRoute();
        }
    
        private synchronized void refreshRoute() {
            try {
                log.info(">>>>>>>>>> start refresh route <<<<<<<<<<");
                if (routeService.refreshRoutes()) {
                    log.info(")))))))))))))))))))))))))))))) FileRefreshRouteService refreshRoute~~~");
                    applicationEventPublisher.publishEvent(new RefreshRoutesEvent(this));
                }
            } catch (IOException e) {
                log.error("Refresh route failed :{}", e.getMessage());
                throw new IllegalStateException("Refresh route failed :{}", e);
            }
        }
    
        @Override
        public void run(String... args) {
            refreshRoute();
        }
    }

    FileDynamicRouteService类

    实现路由刷新的功能,包括checksum路由文件是否修改,是否更新路由

    package com.kawa.spbgateway.service;
    
    import com.kawa.spbgateway.domain.BrianGatewayProperties;
    import com.kawa.spbgateway.property.RefreshRoutePropertySource;
    import com.kawa.spbgateway.transformer.BrianRouteDefinitionTransformer;
    import com.kawa.spbgateway.util.ChecksumUtil;
    import lombok.extern.slf4j.Slf4j;
    import org.springframework.boot.context.properties.bind.Binder;
    import org.springframework.boot.env.PropertySourceLoader;
    import org.springframework.cloud.gateway.route.RouteDefinition;
    import org.springframework.cloud.gateway.route.RouteDefinitionRepository;
    import org.springframework.cloud.gateway.support.NotFoundException;
    import org.springframework.core.env.ConfigurableEnvironment;
    import org.springframework.core.env.Environment;
    import org.springframework.core.env.MutablePropertySources;
    import org.springframework.core.env.PropertySource;
    import org.springframework.core.io.FileSystemResource;
    import org.springframework.core.io.Resource;
    import org.springframework.core.io.support.SpringFactoriesLoader;
    import org.springframework.stereotype.Service;
    import org.springframework.util.CollectionUtils;
    import org.springframework.util.StringUtils;
    import reactor.core.publisher.Flux;
    import reactor.core.publisher.Mono;
    
    import java.io.IOException;
    import java.nio.file.DirectoryStream;
    import java.nio.file.Files;
    import java.nio.file.Path;
    import java.nio.file.Paths;
    import java.util.*;
    import java.util.concurrent.ConcurrentHashMap;
    import java.util.stream.Collectors;
    
    import static com.kawa.spbgateway.content.Contents.*;
    
    @Service
    @Slf4j
    public class FileDynamicRouteService implements RouteDefinitionRepository {
    
        private Environment environment;
    
        private String folder;
    
        private List<String> resourceFileExt;
    
        private ConcurrentHashMap<String, String> fileChecksumMap = new ConcurrentHashMap<>(32);
    
        private ConcurrentHashMap<String, RouteDefinition> routes = new ConcurrentHashMap<>(32);
    
        private BrianRouteDefinitionTransformer transformer = new BrianRouteDefinitionTransformer();
    
        public FileDynamicRouteService(Environment environment) {
            this.environment = environment;
        }
    
        public boolean refreshRoutes() throws IOException {
            getAndInitProperties();
            List<Resource> resources = getCustomizedConfigs();
            if (isRefresh(resources)) {
                updateFileChecksumMap(resources);
                updateRefreshRoutePropertySource(resources);
                refreshRouteCache(readRouteConfig(resources));
                return true;
            }
            log.info(">>>>>>>>>> no need refresh route <<<<<<<<<<");
            return false;
        }
    
        /**
         * @param targets
         */
        private void refreshRouteCache(List<RouteDefinition> targets) {
            // when first load the RouteDefinition
            if (CollectionUtils.isEmpty(routes)) {
                targets.forEach(rd -> {
                    // add routeDefinition
                    save(Mono.just(rd)).subscribe();
                    log.info(">>>>>>>>>> init add routeDefinition:{}", rd);
                });
                return;
            }
    
            List<RouteDefinition> definitions = new ArrayList<>();
            Collections.addAll(definitions, new RouteDefinition[routes.size()]);
            Collections.copy(definitions, routes.values().stream().collect(Collectors.toList()));
    
            targets.forEach(rd -> {
                if (Objects.isNull(routes.get(rd.getId()))) {
                    // add new RouteDefinition
                    save(Mono.just(rd)).subscribe();
                    log.info(">>>>>>>>>> add routeDefinition:{}", rd);
                }
                // not null don't update
                if (Objects.nonNull(routes.get(rd.getId())) && rd.equals(routes.get(rd.getId()))) {
                    definitions.remove(rd);
                }
            });
    
            // remove RouteDefinition
            if (Objects.nonNull(definitions)) {
                definitions.forEach(rd -> {
                    delete(Mono.just(rd.getId())).subscribe();
                    log.info(">>>>>>>>>> delete routeDefinition:{}", rd);
                });
            }
        }
    
        private List<RouteDefinition> readRouteConfig(List<Resource> resources) {
            Binder binder = Binder.get(environment);
            List<RouteDefinition> configs = new ArrayList<>();
            resources.stream().map(res -> res.getFilename()).forEach(fn -> {
                if (!fn.isEmpty()) {
                    log.info(">>>>>>>>>> BrianGatewayProperties filename:{}", fn);
                    BrianGatewayProperties brianGatewayProperties =
                            binder.bindOrCreate(fn, BrianGatewayProperties.class);
                    log.info(">>>>>>>>>> {}", brianGatewayProperties);
                    brianGatewayProperties.getRoutes().forEach(route -> {
                        configs.add(transformer.transform(route, route.getUri() == null ? null : route.getUri().toString()));
                    });
                }
            });
            return configs;
        }
    
    
        private void updateRefreshRoutePropertySource(List<Resource> resources) {
            if (environment instanceof ConfigurableEnvironment) {
                MutablePropertySources propertySources =
                        ((ConfigurableEnvironment) this.environment).getPropertySources();
    
                List<PropertySourceLoader> propertySourceLoaders =
                        SpringFactoriesLoader.loadFactories(PropertySourceLoader.class, getClass().getClassLoader());
                if (null != folder) {
                    resources.forEach(res -> {
                        addCustomizedResource(propertySources, res, propertySourceLoaders);
                    });
                }
            }
        }
    
        /**
         * @param propertySources
         * @param resource
         * @param propertySourceLoaders
         * @return
         */
        private void addCustomizedResource(MutablePropertySources propertySources, Resource resource,
                                           List<PropertySourceLoader> propertySourceLoaders) {
            propertySourceLoaders.forEach(psl -> {
                List<String> fileExts = Arrays.asList(psl.getFileExtensions());
                String filename = resource.getFilename();
                if (fileExts.contains(StringUtils.getFilenameExtension(filename))) {
                    log.info(">>>>>>>>>> load file resource: {}", filename);
                    try {
                        List<PropertySource<?>> propertySourceList = psl.load(filename, resource);
                        propertySourceList.forEach(ps -> {
                            String psName = ps.getName();
                            PropertySource refreshRoutePropertySource = new RefreshRoutePropertySource(psName, ps);
                            propertySources.addLast(refreshRoutePropertySource);
                            log.info(">>>>>>>>>> MutablePropertySources add propertySource: {}", psName);
                        });
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            });
        }
    
        private void updateFileChecksumMap(List<Resource> resources) throws IOException {
            fileChecksumMap.clear();
            for (Resource resource : resources) {
                String fileName = resource.getFile().getName();
                // todo, or can use a easy way that use lastModified replace checksum -> resource.getFile().lastModified();
                String checksum = ChecksumUtil.checkSumByMD5(resource.getFile());
                log.info(">>>>>>>>>> fileName:{},checksum:{}", fileName, checksum);
                fileChecksumMap.put(fileName, checksum);
            }
        }
    
    
        private void getAndInitProperties() {
            if (!StringUtils.hasText(folder)) {
                folder = environment.getProperty(SEARCH_FOLDER_KEY) == null ?
                        environment.getProperty(DEFAULT_FOLDER_KEY) : environment.getProperty(SEARCH_FOLDER_KEY);
                resourceFileExt = Arrays.asList(environment.getProperty(RESOURCE_FILE_EXTENSION_KEY, String[].class,
                        DEFAULT_RESOURCE_FILE_EXTENSIONS));
            }
        }
    
        private List<Resource> getCustomizedConfigs() {
            List<Resource> resources = new ArrayList<>();
            List<String> exclude = Arrays.asList(EXCLUDES);
            try (DirectoryStream<Path> stream = Files.newDirectoryStream(Paths.get(folder))) {
                stream.forEach(path -> {
                    if (!path.toFile().isDirectory() &&
                            resourceFileExt.contains(StringUtils.getFilenameExtension(path.toFile().getName()))
                            && !exclude.contains(path.toFile().getName())
                    ) {
                        log.debug(">>>>>>>>>> load file source: {}", path);
                        resources.add(new FileSystemResource(path));
                    }
                });
            } catch (IOException e) {
                throw new IllegalStateException(String.format("open %s field, %s", folder, e));
            }
            return resources;
        }
    
        private boolean isRefresh(List<Resource> resources) {
    
            if (resources.size() != fileChecksumMap.size()) {
                return true;
            }
    
            if (!Objects.equals(Arrays.asList(fileChecksumMap.keySet().stream().sorted().toArray()),
                    Arrays.asList(resources.stream().map(res -> res.getFilename()).sorted().toArray()))) {
                return true;
            }
            for (Resource resource : resources) {
                try {
                    if (!fileChecksumMap.get(resource.getFilename()).equals(ChecksumUtil.checkSumByMD5(resource.getFile()))) {
                        return true;
                    }
                } catch (IOException e) {
                    log.info(">>>>>>>>>> isRefresh checksum error:{}", e.getMessage());
                }
            }
            return false;
        }
    
        @Override
        public Flux<RouteDefinition> getRouteDefinitions() {
            log.info(")))))))))))))))))))))))))))))) FileDynamicRouteService getRouteDefinitions~~~");
            return Flux.fromIterable(routes.values());
        }
    
        @Override
        public Mono<Void> save(Mono<RouteDefinition> route) {
            return route.flatMap(r -> {
                routes.put(r.getId(), r);
                return Mono.empty();
            });
        }
    
        @Override
        public Mono<Void> delete(Mono<String> routeId) {
            return routeId.flatMap(id -> {
                log.debug(">>>>>>>>>> remove the RouteDefinition id is: {}", id);
                if (routes.keySet().contains(id)) {
                    routes.remove(id);
                    return Mono.empty();
                }
                return Mono.defer(() -> Mono.error(new NotFoundException(String.format("RouteDefinition not found -> %s", routeId))));
            });
        }
    }

    BrianRouteDefinition类 

    主要是为了扩展RouteDefinition,添加一些新的路由配置属性

    package com.kawa.spbgateway.route;
    
    import lombok.Data;
    import org.springframework.cloud.gateway.route.RouteDefinition;
    import org.springframework.validation.annotation.Validated;
    
    import java.util.ArrayList;
    import java.util.List;
    import java.util.Objects;
    
    @Validated
    @Data
    public class BrianRouteDefinition extends RouteDefinition {
        private List<String> apiKeys = new ArrayList<>();
        private List<String> auths = new ArrayList<>();
    
        @Override
        public int hashCode() {
            return Objects.hash(getId(), getPredicates(), getFilters(), getUri(), getMetadata(), getOrder(),
                    this.apiKeys, this.auths);
        }
    
        @Override
        public String toString() {
            return "{" +
                    "id=" + getId() +
                    ", uri=" + getUri() +
                    ", predicates=" + getPredicates() +
                    ", filters=" + getFilters() +
                    ", metadata=" + getMetadata() +
                    ", order=" + getOrder() +
                    ", apiKeys=" + apiKeys +
                    ", auths=" + auths +
                    '}';
        }
    }

    BrianConfigGatewayFilterFactory类

    该类是为了处理BrianRouteDefinition里面的属性,我这边就简单的赋值给exchange

    package com.kawa.spbgateway.filter;
    
    import lombok.extern.slf4j.Slf4j;
    import org.springframework.cloud.gateway.filter.GatewayFilter;
    import org.springframework.cloud.gateway.filter.OrderedGatewayFilter;
    import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory;
    import org.springframework.stereotype.Component;
    import org.springframework.web.server.ServerWebExchange;
    
    import java.util.List;
    import java.util.Objects;
    
    import static com.kawa.spbgateway.content.Contents.*;
    
    @Slf4j
    @Component
    public class BrianConfigGatewayFilterFactory extends AbstractGatewayFilterFactory<BrianConfigGatewayFilterFactory.Config> {
    
        public BrianConfigGatewayFilterFactory() {
            super(Config.class);
        }
    
        @Override
        public GatewayFilter apply(Config config) {
            return new OrderedGatewayFilter((exchange, chain) -> {
                initExchangeAttr(config, exchange);
                return chain.filter(exchange);
            }, 120);
        }
    
        private void initExchangeAttr(Config config, ServerWebExchange exchange) {
            if (Objects.nonNull(config.getAuths())) {
                exchange.getAttributes().put(GATEWAY_CONFIG_CLASS_AUTH, config.getAuths());
            }
            if (Objects.requireNonNull(config.getApiKeys()).size() > 0) {
                exchange.getAttributes().put(GATEWAY_CONFIG_CLASS_API_KEYS, config.getApiKeys());
            }
        }
    
        public static class Config {
            private String[] auths;
            private List<String> apiKeys;
    
            public String[] getAuths() {
                return auths;
            }
    
            public void setAuths(String[] auths) {
                this.auths = auths;
            }
    
            public List<String> getApiKeys() {
                return apiKeys;
            }
    
            public void setApiKeys(List<String> apiKeys) {
                this.apiKeys = apiKeys;
            }
        }
    }

    RefreshRoutePropertySource类

    自定义一个PropertySpurce加了自己的前缀,此处为了方便自己识别,也方便自己管理在内存中的路由

    package com.kawa.spbgateway.property;
    
    
    import org.springframework.core.env.PropertySource;
    import org.springframework.util.Assert;
    
    import java.util.Objects;
    
    /**
     * RefreshRoutePropertySource
     * add a prefix for an existing property source
     */
    public class RefreshRoutePropertySource extends PropertySource {
    
        private PropertySource innerPropertySource;
        private String prefix;
    
        public RefreshRoutePropertySource(String prefix, PropertySource origin) {
            super("RefreshRoutePropertySource-" + origin.getName());
            this.innerPropertySource = origin;
            this.prefix = prefix;
        }
    
        @Override
        public Object getProperty(String name) {
            Assert.notNull(name, "name can not be null!");
            var target = prefix + ".";
            if (name.startsWith(target)) {
                return innerPropertySource.getProperty(name.replace(target, ""));
            }
            return null;
        }
    
        @Override
        public boolean equals(Object o) {
            if (this == o) return true;
            if (o == null || getClass() != o.getClass()) return false;
            if (!super.equals(o)) return false;
            RefreshRoutePropertySource that = (RefreshRoutePropertySource) o;
            return innerPropertySource.equals(that.innerPropertySource) && prefix.equals(that.prefix);
        }
    
        @Override
        public int hashCode() {
            return Objects.hash(super.hashCode(), innerPropertySource, prefix);
        }
    }

    BrianGatewayProperties类

    配合Springboot的Binder从内存获取自定义的路由BrianRouteDefinition

    package com.kawa.spbgateway.domain;
    
    import com.kawa.spbgateway.route.BrianRouteDefinition;
    import lombok.Data;
    
    import java.util.ArrayList;
    import java.util.List;
    
    @Data
    public class BrianGatewayProperties {
        private String url;
        private List<BrianRouteDefinition> routes =new ArrayList<>();
    }

    BrianRouteDefinitionTransformer类

    将路由配置文件读取的配置信息,赋值给BrianRouteDefinition

    package com.kawa.spbgateway.transformer;
    
    import com.kawa.spbgateway.config.ApiKeysConfiguration;
    import com.kawa.spbgateway.route.BrianRouteDefinition;
    import lombok.extern.slf4j.Slf4j;
    import org.springframework.cloud.gateway.filter.FilterDefinition;
    import org.springframework.cloud.gateway.handler.predicate.PredicateDefinition;
    import org.springframework.cloud.gateway.route.RouteDefinition;
    import org.springframework.util.StringUtils;
    
    import java.net.URI;
    import java.util.*;
    import java.util.regex.Matcher;
    import java.util.regex.Pattern;
    import java.util.stream.Collectors;
    
    import static com.kawa.spbgateway.content.Contents.*;
    
    @Slf4j
    public class BrianRouteDefinitionTransformer {
        private String defaultRewritePathRegexp = "^/api/(?<domain>[a-zA-Z-]*)/v(?<version>[0-9])/(?<path>.*)";
        private String defaultRewritePathReplacement = "/v$\{version}/$\{path}";
        private String extendRewritePathRegexp = "^/api/(?<region>[a-zA-Z-]*)/(?<domain>[a-zA-Z-]*)/v(?<version>[0-9])/(?<path>.*)";
        private String extendRewritePathReplacement = "/v$\{version}/$\{path}";
    
        private String defaultRewriteDomainRegexp = "^/api/(?<domain>[a-zA-Z-]*)/v.+/.*";
        private String defaultRewriteDomainReplacement = "https://$\{domain}.free.beeceptor.com";
        private String extendRewriteDomainRegexp = "^/api/(?<region>[a-zA-Z-]*)/(?<domain>[a-zA-Z-]*)/v.+/.*";
        private String extendRewriteDomainReplacement = "https://$\{domain}-$\{region}.free.beeceptor.com";
    
        private List<String> default1FAValues = Arrays.asList("pwd", "sms", "gAuth");
    
        private List<String> default2FAValues = Arrays.asList("pwd+sms", "sms+gAuth");
    
        private ApiKeysConfiguration apiKeys;
    
        public RouteDefinition transform(BrianRouteDefinition brianRouteDefinition, String uri) {
            // add ConfigGatewayFilter
            FilterDefinition configFilter = new FilterDefinition();
            configFilter.setName(CONFIG_GATEWAY_FILTER_CLASS_NAME);
            HashMap<String, String> configArgs = new HashMap<>();
    
            var apiKeyString = brianRouteDefinition.getApiKeys().stream().map(ak -> apiKeys.getValue(ak)).collect(Collectors.toList()).toString();
            configArgs.put(GATEWAY_CONFIG_CLASS_API_KEYS, apiKeyString.substring(1, apiKeyString.length() - 1));
    
            configArgs.put(GATEWAY_CONFIG_CLASS_AUTH, default1FAValues.toString());
            if (Objects.nonNull(brianRouteDefinition.getAuths()) &&
                    brianRouteDefinition.getAuths().size() > 0) {
                configArgs.put(GATEWAY_CONFIG_CLASS_AUTH, brianRouteDefinition.getAuths().toString());
            }
            configFilter.setArgs(configArgs);
            brianRouteDefinition.getFilters().add(configFilter);
    
            if (StringUtils.hasText(uri)) {
                brianRouteDefinition.setUri(URI.create(uri));
                // set route id
                setRouteId(brianRouteDefinition);
            }
            long count = brianRouteDefinition.getFilters().stream()
                    .filter(filterDefinition -> filterDefinition.getName().equals(REWRITE_GATEWAY_FILTER_CLASS_NAME))
                    .count();
            // get path value from Prediction config
            var path = getPathString(brianRouteDefinition);
            log.info(">>>>>>>>>> route path: {}", path);
            var replacement = defaultRewriteDomainReplacement.replace("$\", "$");
            Pattern pattern = Pattern.compile(defaultRewriteDomainRegexp);
            Matcher defaultMatcher = pattern.matcher(path);
            if (defaultMatcher.matches()) {
                String newDomain = defaultMatcher.replaceAll(replacement);
                log.info(">>>>>>>>>>  redefine the path {{}} and new domain {{}}", path, newDomain);
                if (Objects.isNull(brianRouteDefinition.getUri())) {
                    brianRouteDefinition.setUri(URI.create(newDomain));
                    // set route id
                    setRouteId(brianRouteDefinition);
                }
                // add RewritePathGatewayFilter
                if (count < 1L) {
                    addRewriteFilter(brianRouteDefinition, defaultRewritePathRegexp, defaultRewritePathReplacement);
                }
                return brianRouteDefinition;
            }
    
            var replacementExt = extendRewriteDomainReplacement.replace("$\", "$");
            Pattern patternExt = Pattern.compile(extendRewriteDomainRegexp);
            Matcher defaultExtMatcher = patternExt.matcher(path);
            if (defaultExtMatcher.matches()) {
                String newDomain = defaultExtMatcher.replaceAll(replacementExt);
                if (Objects.isNull(brianRouteDefinition.getUri())) {
                    brianRouteDefinition.setUri(URI.create(newDomain));
                    // set route id
                    setRouteId(brianRouteDefinition);
                }
                // add RewritePathGatewayFilter
                if (count < 1L) {
                    addRewriteFilter(brianRouteDefinition, extendRewritePathRegexp, extendRewritePathReplacement);
                }
                return brianRouteDefinition;
            }
            if (Objects.isNull(brianRouteDefinition.getUri())) {
                brianRouteDefinition.setUri(URI.create(FALL_BACK_URI + path));
                // set route id
                setRouteId(brianRouteDefinition);
            }
            return brianRouteDefinition;
        }
    
        private void setRouteId(BrianRouteDefinition customizedRouteDefinition) {
            String url = customizedRouteDefinition.getUri().toString();
            customizedRouteDefinition.setId(String.format("%s@%s", url, customizedRouteDefinition.hashCode()));
        }
    
        private void addRewriteFilter(BrianRouteDefinition customizedRouteDefinition, String rewritePathRegexp, String rewritePathReplacement) {
            FilterDefinition rewriteFilter = new FilterDefinition();
            rewriteFilter.setName(REWRITE_GATEWAY_FILTER_CLASS_NAME);
            HashMap<String, String> rewriteFilterArgs = new HashMap<>();
            rewriteFilterArgs.put(REWRITE_GATEWAY_FILTER_REGEXP, rewritePathRegexp);
            rewriteFilterArgs.put(REWRITE_GATEWAY_FILTER_REPLACEMENT, rewritePathReplacement);
            rewriteFilter.setArgs(rewriteFilterArgs);
            customizedRouteDefinition.getFilters().add(rewriteFilter);
        }
    
        private String getPathString(BrianRouteDefinition customizedRouteDefinition) {
            for (PredicateDefinition predicateDefinition : customizedRouteDefinition.getPredicates()) {
                if (PREDICATE_PATH.equals(predicateDefinition.getName())) {
                    var firstKey = predicateDefinition.getArgs().keySet().iterator().next();
                    return predicateDefinition.getArgs().get(firstKey);
                }
            }
            return FALL_BACK_URI;
        }
    
    }

    ok,测试下,启动项目

    INFO   [restartedMain] 2021-09-12 21:11:11.451 c.k.s.s.FileRefreshRouteService - >>>>>>>>>> start refresh route <<<<<<<<<<
    DEBUG  [restartedMain] 2021-09-12 21:11:11.455 c.k.s.s.FileDynamicRouteService - >>>>>>>>>> load file source: /home/un/code/springBoot_2017/spb-demo/spb-gateway/src/main/resources/card-hk.yml
    DEBUG  [restartedMain] 2021-09-12 21:11:11.455 c.k.s.s.FileDynamicRouteService - >>>>>>>>>> load file source: /home/un/code/springBoot_2017/spb-demo/spb-gateway/src/main/resources/pancake.yml
    DEBUG  [restartedMain] 2021-09-12 21:11:11.455 c.k.s.s.FileDynamicRouteService - >>>>>>>>>> load file source: /home/un/code/springBoot_2017/spb-demo/spb-gateway/src/main/resources/passport-hk.yml
    INFO   [restartedMain] 2021-09-12 21:11:11.463 c.k.s.s.FileDynamicRouteService - >>>>>>>>>> fileName:card-hk.yml,checksum:3867921742
    INFO   [restartedMain] 2021-09-12 21:11:11.463 c.k.s.s.FileDynamicRouteService - >>>>>>>>>> fileName:pancake.yml,checksum:2400413005
    INFO   [restartedMain] 2021-09-12 21:11:11.464 c.k.s.s.FileDynamicRouteService - >>>>>>>>>> fileName:passport-hk.yml,checksum:140450225
    INFO   [restartedMain] 2021-09-12 21:11:11.465 c.k.s.s.FileDynamicRouteService - >>>>>>>>>> load file resource: card-hk.yml
    DEBUG  [restartedMain] 2021-09-12 21:11:11.492 o.s.boot.env.OriginTrackedYamlLoader - Loading from YAML: file [/home/un/code/springBoot_2017/spb-demo/spb-gateway/src/main/resources/card-hk.yml]
    DEBUG  [restartedMain] 2021-09-12 21:11:11.514 o.s.boot.env.OriginTrackedYamlLoader - Merging document (no matchers set): {routes=[{uri=http://card-hk.${gateway.route.domain.postfix}, predicates=[Path=/api/hk/card/v1/uuu/query, Method=POST]}, {uri=http://card-hk.${gateway.route.domain.postfix}, predicates=[Path=/api/hk/card/v1/er/query, Method=POST]}]}
    DEBUG  [restartedMain] 2021-09-12 21:11:11.515 o.s.boot.env.OriginTrackedYamlLoader - Loaded 1 document from YAML resource: file [/home/un/code/springBoot_2017/spb-demo/spb-gateway/src/main/resources/card-hk.yml]
    INFO   [restartedMain] 2021-09-12 21:11:11.516 c.k.s.s.FileDynamicRouteService - >>>>>>>>>> MutablePropertySources add propertySource: card-hk.yml
    INFO   [restartedMain] 2021-09-12 21:11:11.516 c.k.s.s.FileDynamicRouteService - >>>>>>>>>> load file resource: pancake.yml
    DEBUG  [restartedMain] 2021-09-12 21:11:11.516 o.s.boot.env.OriginTrackedYamlLoader - Loading from YAML: file [/home/un/code/springBoot_2017/spb-demo/spb-gateway/src/main/resources/pancake.yml]
    DEBUG  [restartedMain] 2021-09-12 21:11:11.517 o.s.boot.env.OriginTrackedYamlLoader - Merging document (no matchers set): {routes=[{uri=http://pancake.${gateway.route.domain.postfix}, predicates=[Path=^/api/pancake/v1/*,^/api/pancake/v1/*/query]}, {predicates=[Path=/api/pancake/v1/coin/query], filters=[RewritePath=/api/pancake/v1/coin/query, /api/v1/coin/query]}]}
    DEBUG  [restartedMain] 2021-09-12 21:11:11.518 o.s.boot.env.OriginTrackedYamlLoader - Loaded 1 document from YAML resource: file [/home/un/code/springBoot_2017/spb-demo/spb-gateway/src/main/resources/pancake.yml]
    INFO   [restartedMain] 2021-09-12 21:11:11.518 c.k.s.s.FileDynamicRouteService - >>>>>>>>>> MutablePropertySources add propertySource: pancake.yml
    INFO   [restartedMain] 2021-09-12 21:11:11.518 c.k.s.s.FileDynamicRouteService - >>>>>>>>>> load file resource: passport-hk.yml
    DEBUG  [restartedMain] 2021-09-12 21:11:11.519 o.s.boot.env.OriginTrackedYamlLoader - Loading from YAML: file [/home/un/code/springBoot_2017/spb-demo/spb-gateway/src/main/resources/passport-hk.yml]
    DEBUG  [restartedMain] 2021-09-12 21:11:11.520 o.s.boot.env.OriginTrackedYamlLoader - Merging document (no matchers set): {routes=[{uri=http://passport-hk.${gateway.route.domain.postfix}, predicates=[Path=/api/passport-hk/v1/passport/query], auths=[sms]}]}
    DEBUG  [restartedMain] 2021-09-12 21:11:11.520 o.s.boot.env.OriginTrackedYamlLoader - Loaded 1 document from YAML resource: file [/home/un/code/springBoot_2017/spb-demo/spb-gateway/src/main/resources/passport-hk.yml]
    INFO   [restartedMain] 2021-09-12 21:11:11.521 c.k.s.s.FileDynamicRouteService - >>>>>>>>>> MutablePropertySources add propertySource: passport-hk.yml
    INFO   [restartedMain] 2021-09-12 21:11:11.522 c.k.s.s.FileDynamicRouteService - >>>>>>>>>> BrianGatewayProperties filename:card-hk.yml
    INFO   [restartedMain] 2021-09-12 21:11:11.531 c.k.s.s.FileDynamicRouteService - >>>>>>>>>> BrianGatewayProperties(url=null, routes=[{id=null, uri=http://card-hk.free.beeceptor.com, predicates=[PredicateDefinition{name='Path', args={_genkey_0=/api/hk/card/v1/uuu/query}}, PredicateDefinition{name='Method', args={_genkey_0=POST}}], filters=[], metadata={}, order=0, apiKeys=[], auths=[]}, {id=null, uri=http://card-hk.free.beeceptor.com, predicates=[PredicateDefinition{name='Path', args={_genkey_0=/api/hk/card/v1/er/query}}, PredicateDefinition{name='Method', args={_genkey_0=POST}}], filters=[], metadata={}, order=0, apiKeys=[], auths=[]}])
    INFO   [restartedMain] 2021-09-12 21:11:11.553 c.k.s.t.BrianRouteDefinitionTransformer - >>>>>>>>>> route path: /api/hk/card/v1/uuu/query
    INFO   [restartedMain] 2021-09-12 21:11:11.554 c.k.s.t.BrianRouteDefinitionTransformer - >>>>>>>>>> route path: /api/hk/card/v1/er/query
    INFO   [restartedMain] 2021-09-12 21:11:11.554 c.k.s.s.FileDynamicRouteService - >>>>>>>>>> BrianGatewayProperties filename:pancake.yml
    INFO   [restartedMain] 2021-09-12 21:11:11.565 c.k.s.s.FileDynamicRouteService - >>>>>>>>>> BrianGatewayProperties(url=null, routes=[{id=null, uri=http://pancake.free.beeceptor.com, predicates=[PredicateDefinition{name='Path', args={_genkey_0=^/api/pancake/v1/*, _genkey_1=^/api/pancake/v1/*/query}}], filters=[], metadata={}, order=0, apiKeys=[], auths=[]}, {id=null, uri=null, predicates=[PredicateDefinition{name='Path', args={_genkey_0=/api/pancake/v1/coin/query}}], filters=[FilterDefinition{name='RewritePath', args={_genkey_0=/api/pancake/v1/coin/query, _genkey_1=/api/v1/coin/query}}], metadata={}, order=0, apiKeys=[], auths=[]}])
    INFO   [restartedMain] 2021-09-12 21:11:11.565 c.k.s.t.BrianRouteDefinitionTransformer - >>>>>>>>>> route path: ^/api/pancake/v1/*
    INFO   [restartedMain] 2021-09-12 21:11:11.566 c.k.s.t.BrianRouteDefinitionTransformer - >>>>>>>>>> route path: /api/pancake/v1/coin/query
    INFO   [restartedMain] 2021-09-12 21:11:11.566 c.k.s.t.BrianRouteDefinitionTransformer - >>>>>>>>>>  redefine the path {/api/pancake/v1/coin/query} and new domain {https://pancake.free.beeceptor.com}
    INFO   [restartedMain] 2021-09-12 21:11:11.566 c.k.s.s.FileDynamicRouteService - >>>>>>>>>> BrianGatewayProperties filename:passport-hk.yml
    INFO   [restartedMain] 2021-09-12 21:11:11.574 c.k.s.s.FileDynamicRouteService - >>>>>>>>>> BrianGatewayProperties(url=null, routes=[{id=null, uri=http://passport-hk.free.beeceptor.com, predicates=[PredicateDefinition{name='Path', args={_genkey_0=/api/passport-hk/v1/passport/query}}], filters=[], metadata={}, order=0, apiKeys=[], auths=[sms]}])
    INFO   [restartedMain] 2021-09-12 21:11:11.575 c.k.s.t.BrianRouteDefinitionTransformer - >>>>>>>>>> route path: /api/passport-hk/v1/passport/query
    INFO   [restartedMain] 2021-09-12 21:11:11.575 c.k.s.t.BrianRouteDefinitionTransformer - >>>>>>>>>>  redefine the path {/api/passport-hk/v1/passport/query} and new domain {https://passport-hk.free.beeceptor.com}
    INFO   [restartedMain] 2021-09-12 21:11:11.577 c.k.s.s.FileDynamicRouteService - >>>>>>>>>> init add routeDefinition:{id=http://card-hk.free.beeceptor.com@1548203624, uri=http://card-hk.free.beeceptor.com, predicates=[PredicateDefinition{name='Path', args={_genkey_0=/api/hk/card/v1/uuu/query}}, PredicateDefinition{name='Method', args={_genkey_0=POST}}], filters=[FilterDefinition{name='BrianConfig', args={auths=[pwd, sms, gAuth], apiKeys=}}, FilterDefinition{name='RewritePath', args={regexp=^/api/(?<region>[a-zA-Z-]*)/(?<domain>[a-zA-Z-]*)/v(?<version>[0-9])/(?<path>.*), replacement=/v${version}/${path}}}], metadata={}, order=0, apiKeys=[], auths=[]}
    INFO   [restartedMain] 2021-09-12 21:11:11.577 c.k.s.s.FileDynamicRouteService - >>>>>>>>>> init add routeDefinition:{id=http://card-hk.free.beeceptor.com@-679151078, uri=http://card-hk.free.beeceptor.com, predicates=[PredicateDefinition{name='Path', args={_genkey_0=/api/hk/card/v1/er/query}}, PredicateDefinition{name='Method', args={_genkey_0=POST}}], filters=[FilterDefinition{name='BrianConfig', args={auths=[pwd, sms, gAuth], apiKeys=}}, FilterDefinition{name='RewritePath', args={regexp=^/api/(?<region>[a-zA-Z-]*)/(?<domain>[a-zA-Z-]*)/v(?<version>[0-9])/(?<path>.*), replacement=/v${version}/${path}}}], metadata={}, order=0, apiKeys=[], auths=[]}
    INFO   [restartedMain] 2021-09-12 21:11:11.577 c.k.s.s.FileDynamicRouteService - >>>>>>>>>> init add routeDefinition:{id=http://pancake.free.beeceptor.com@-1468813552, uri=http://pancake.free.beeceptor.com, predicates=[PredicateDefinition{name='Path', args={_genkey_0=^/api/pancake/v1/*, _genkey_1=^/api/pancake/v1/*/query}}], filters=[FilterDefinition{name='BrianConfig', args={auths=[pwd, sms, gAuth], apiKeys=}}], metadata={}, order=0, apiKeys=[], auths=[]}
    INFO   [restartedMain] 2021-09-12 21:11:11.577 c.k.s.s.FileDynamicRouteService - >>>>>>>>>> init add routeDefinition:{id=https://pancake.free.beeceptor.com@1912448187, uri=https://pancake.free.beeceptor.com, predicates=[PredicateDefinition{name='Path', args={_genkey_0=/api/pancake/v1/coin/query}}], filters=[FilterDefinition{name='RewritePath', args={_genkey_0=/api/pancake/v1/coin/query, _genkey_1=/api/v1/coin/query}}, FilterDefinition{name='BrianConfig', args={auths=[pwd, sms, gAuth], apiKeys=}}], metadata={}, order=0, apiKeys=[], auths=[]}
    INFO   [restartedMain] 2021-09-12 21:11:11.577 c.k.s.s.FileDynamicRouteService - >>>>>>>>>> init add routeDefinition:{id=http://passport-hk.free.beeceptor.com@1466789461, uri=http://passport-hk.free.beeceptor.com, predicates=[PredicateDefinition{name='Path', args={_genkey_0=/api/passport-hk/v1/passport/query}}], filters=[FilterDefinition{name='BrianConfig', args={auths=[sms], apiKeys=}}, FilterDefinition{name='RewritePath', args={regexp=^/api/(?<domain>[a-zA-Z-]*)/v(?<version>[0-9])/(?<path>.*), replacement=/v${version}/${path}}}], metadata={}, order=0, apiKeys=[], auths=[sms]}
    INFO   [restartedMain] 2021-09-12 21:11:11.578 c.k.s.s.FileRefreshRouteService - )))))))))))))))))))))))))))))) FileRefreshRouteService refreshRoute~~~
    INFO   [restartedMain] 2021-09-12 21:11:11.578 c.k.s.s.FileDynamicRouteService - )))))))))))))))))))))))))))))) FileDynamicRouteService getRouteDefinitions~~~
    DEBUG  [restartedMain] 2021-09-12 21:11:11.579 o.s.c.g.r.RouteDefinitionRouteLocator - RouteDefinition http://card-hk.free.beeceptor.com@1548203624 applying {_genkey_0=/api/hk/card/v1/uuu/query} to Path
    DEBUG  [restartedMain] 2021-09-12 21:11:11.627 o.s.c.g.r.RouteDefinitionRouteLocator - RouteDefinition http://card-hk.free.beeceptor.com@1548203624 applying {_genkey_0=POST} to Method
    DEBUG  [restartedMain] 2021-09-12 21:11:11.634 o.s.c.g.r.RouteDefinitionRouteLocator - RouteDefinition http://card-hk.free.beeceptor.com@1548203624 applying filter {auths=[pwd, sms, gAuth], apiKeys=} to BrianConfig
    DEBUG  [restartedMain] 2021-09-12 21:11:11.642 o.s.c.g.r.RouteDefinitionRouteLocator - RouteDefinition http://card-hk.free.beeceptor.com@1548203624 applying filter {regexp=^/api/(?<region>[a-zA-Z-]*)/(?<domain>[a-zA-Z-]*)/v(?<version>[0-9])/(?<path>.*), replacement=/v${version}/${path}} to RewritePath
    DEBUG  [restartedMain] 2021-09-12 21:11:11.679 o.s.c.g.r.RouteDefinitionRouteLocator - RouteDefinition matched: http://card-hk.free.beeceptor.com@1548203624
    DEBUG  [restartedMain] 2021-09-12 21:11:11.679 o.s.c.g.r.RouteDefinitionRouteLocator - RouteDefinition http://passport-hk.free.beeceptor.com@1466789461 applying {_genkey_0=/api/passport-hk/v1/passport/query} to Path
    DEBUG  [restartedMain] 2021-09-12 21:11:11.681 o.s.c.g.r.RouteDefinitionRouteLocator - RouteDefinition http://passport-hk.free.beeceptor.com@1466789461 applying filter {auths=[sms], apiKeys=} to BrianConfig
    DEBUG  [restartedMain] 2021-09-12 21:11:11.682 o.s.c.g.r.RouteDefinitionRouteLocator - RouteDefinition http://passport-hk.free.beeceptor.com@1466789461 applying filter {regexp=^/api/(?<domain>[a-zA-Z-]*)/v(?<version>[0-9])/(?<path>.*), replacement=/v${version}/${path}} to RewritePath
    DEBUG  [restartedMain] 2021-09-12 21:11:11.683 o.s.c.g.r.RouteDefinitionRouteLocator - RouteDefinition matched: http://passport-hk.free.beeceptor.com@1466789461
    DEBUG  [restartedMain] 2021-09-12 21:11:11.684 o.s.c.g.r.RouteDefinitionRouteLocator - RouteDefinition http://pancake.free.beeceptor.com@-1468813552 applying {_genkey_0=^/api/pancake/v1/*, _genkey_1=^/api/pancake/v1/*/query} to Path
    DEBUG  [restartedMain] 2021-09-12 21:11:11.686 o.s.c.g.r.RouteDefinitionRouteLocator - RouteDefinition http://pancake.free.beeceptor.com@-1468813552 applying filter {auths=[pwd, sms, gAuth], apiKeys=} to BrianConfig
    DEBUG  [restartedMain] 2021-09-12 21:11:11.688 o.s.c.g.r.RouteDefinitionRouteLocator - RouteDefinition matched: http://pancake.free.beeceptor.com@-1468813552
    DEBUG  [restartedMain] 2021-09-12 21:11:11.688 o.s.c.g.r.RouteDefinitionRouteLocator - RouteDefinition https://pancake.free.beeceptor.com@1912448187 applying {_genkey_0=/api/pancake/v1/coin/query} to Path
    DEBUG  [restartedMain] 2021-09-12 21:11:11.689 o.s.c.g.r.RouteDefinitionRouteLocator - RouteDefinition https://pancake.free.beeceptor.com@1912448187 applying filter {_genkey_0=/api/pancake/v1/coin/query, _genkey_1=/api/v1/coin/query} to RewritePath
    DEBUG  [restartedMain] 2021-09-12 21:11:11.691 o.s.c.g.r.RouteDefinitionRouteLocator - RouteDefinition https://pancake.free.beeceptor.com@1912448187 applying filter {auths=[pwd, sms, gAuth], apiKeys=} to BrianConfig
    DEBUG  [restartedMain] 2021-09-12 21:11:11.693 o.s.c.g.r.RouteDefinitionRouteLocator - RouteDefinition matched: https://pancake.free.beeceptor.com@1912448187
    DEBUG  [restartedMain] 2021-09-12 21:11:11.693 o.s.c.g.r.RouteDefinitionRouteLocator - RouteDefinition http://card-hk.free.beeceptor.com@-679151078 applying {_genkey_0=/api/hk/card/v1/er/query} to Path
    DEBUG  [restartedMain] 2021-09-12 21:11:11.695 o.s.c.g.r.RouteDefinitionRouteLocator - RouteDefinition http://card-hk.free.beeceptor.com@-679151078 applying {_genkey_0=POST} to Method
    DEBUG  [restartedMain] 2021-09-12 21:11:11.696 o.s.c.g.r.RouteDefinitionRouteLocator - RouteDefinition http://card-hk.free.beeceptor.com@-679151078 applying filter {auths=[pwd, sms, gAuth], apiKeys=} to BrianConfig
    DEBUG  [restartedMain] 2021-09-12 21:11:11.698 o.s.c.g.r.RouteDefinitionRouteLocator - RouteDefinition http://card-hk.free.beeceptor.com@-679151078 applying filter {regexp=^/api/(?<region>[a-zA-Z-]*)/(?<domain>[a-zA-Z-]*)/v(?<version>[0-9])/(?<path>.*), replacement=/v${version}/${path}} to RewritePath
    DEBUG  [restartedMain] 2021-09-12 21:11:11.700 o.s.c.g.r.RouteDefinitionRouteLocator - RouteDefinition matched: http://card-hk.free.beeceptor.com@-679151078

    查看日志可以看到启动后加载路由的配置,然后每个10秒会定时检查是否刷新,日志如下

    INFO   [scheduling-1] 2021-09-12 21:16:20.000 c.k.s.s.FileRefreshRouteService - >>>>>>>>>> start refresh route <<<<<<<<<<
    DEBUG  [scheduling-1] 2021-09-12 21:16:20.001 c.k.s.s.FileDynamicRouteService - >>>>>>>>>> load file source: /home/un/code/springBoot_2017/spb-demo/spb-gateway/src/main/resources/card-hk.yml
    DEBUG  [scheduling-1] 2021-09-12 21:16:20.001 c.k.s.s.FileDynamicRouteService - >>>>>>>>>> load file source: /home/un/code/springBoot_2017/spb-demo/spb-gateway/src/main/resources/pancake.yml
    DEBUG  [scheduling-1] 2021-09-12 21:16:20.001 c.k.s.s.FileDynamicRouteService - >>>>>>>>>> load file source: /home/un/code/springBoot_2017/spb-demo/spb-gateway/src/main/resources/passport-hk.yml
    INFO   [scheduling-1] 2021-09-12 21:16:20.002 c.k.s.s.FileDynamicRouteService - >>>>>>>>>> no need refresh route <<<<<<<<<<
    INFO   [scheduling-1] 2021-09-12 21:16:25.000 c.k.s.s.FileRefreshRouteService - >>>>>>>>>> start refresh route <<<<<<<<<<
    DEBUG  [scheduling-1] 2021-09-12 21:16:25.001 c.k.s.s.FileDynamicRouteService - >>>>>>>>>> load file source: /home/un/code/springBoot_2017/spb-demo/spb-gateway/src/main/resources/card-hk.yml
    DEBUG  [scheduling-1] 2021-09-12 21:16:25.001 c.k.s.s.FileDynamicRouteService - >>>>>>>>>> load file source: /home/un/code/springBoot_2017/spb-demo/spb-gateway/src/main/resources/pancake.yml
    DEBUG  [scheduling-1] 2021-09-12 21:16:25.001 c.k.s.s.FileDynamicRouteService - >>>>>>>>>> load file source: /home/un/code/springBoot_2017/spb-demo/spb-gateway/src/main/resources/passport-hk.yml
    INFO   [scheduling-1] 2021-09-12 21:16:25.003 c.k.s.s.FileDynamicRouteService - >>>>>>>>>> no need refresh route <<<<<<<<<<

    然后,通过actuator的接口查看,可以看到路由已经生效了

    ok,来测试下路由/api/passport-hk/v1/passport/query,可以看到路由生效的

    日志也打印相关日志 

    测试下修改路由是否生效(添加和删除路由配置,还有修改路由文件名,在这里不演示了,代码已经测试过了)

    通过日志发现有路由的刷新日志

    INFO   [scheduling-1] 2021-09-12 21:33:55.001 c.k.s.s.FileRefreshRouteService - >>>>>>>>>> start refresh route <<<<<<<<<<
    DEBUG  [scheduling-1] 2021-09-12 21:33:55.002 c.k.s.s.FileDynamicRouteService - >>>>>>>>>> load file source: /home/un/code/springBoot_2017/spb-demo/spb-gateway/src/main/resources/card-hk.yml
    DEBUG  [scheduling-1] 2021-09-12 21:33:55.002 c.k.s.s.FileDynamicRouteService - >>>>>>>>>> load file source: /home/un/code/springBoot_2017/spb-demo/spb-gateway/src/main/resources/pancake.yml
    DEBUG  [scheduling-1] 2021-09-12 21:33:55.003 c.k.s.s.FileDynamicRouteService - >>>>>>>>>> load file source: /home/un/code/springBoot_2017/spb-demo/spb-gateway/src/main/resources/passport-hk.yml
    INFO   [scheduling-1] 2021-09-12 21:33:55.004 c.k.s.s.FileDynamicRouteService - >>>>>>>>>> fileName:card-hk.yml,checksum:56354776
    INFO   [scheduling-1] 2021-09-12 21:33:55.004 c.k.s.s.FileDynamicRouteService - >>>>>>>>>> fileName:pancake.yml,checksum:2400413005
    INFO   [scheduling-1] 2021-09-12 21:33:55.004 c.k.s.s.FileDynamicRouteService - >>>>>>>>>> fileName:passport-hk.yml,checksum:1148328829
    INFO   [scheduling-1] 2021-09-12 21:33:55.004 c.k.s.s.FileDynamicRouteService - >>>>>>>>>> load file resource: card-hk.yml
    DEBUG  [scheduling-1] 2021-09-12 21:33:55.005 o.s.boot.env.OriginTrackedYamlLoader - Loading from YAML: file [/home/un/code/springBoot_2017/spb-demo/spb-gateway/src/main/resources/card-hk.yml]
    DEBUG  [scheduling-1] 2021-09-12 21:33:55.008 o.s.boot.env.OriginTrackedYamlLoader - Merging document (no matchers set): {routes=[{uri=http://card-hk.${gateway.route.domain.postfix}, predicates=[Path=/api/hk/card/v1/card/query, Method=POST]}, {uri=http://card-hk.${gateway.route.domain.postfix}, predicates=[Path=/api/hk/card/v1/er/query, Method=POST]}]}
    DEBUG  [scheduling-1] 2021-09-12 21:33:55.008 o.s.boot.env.OriginTrackedYamlLoader - Loaded 1 document from YAML resource: file [/home/un/code/springBoot_2017/spb-demo/spb-gateway/src/main/resources/card-hk.yml]
    INFO   [scheduling-1] 2021-09-12 21:33:55.009 c.k.s.s.FileDynamicRouteService - >>>>>>>>>> MutablePropertySources add propertySource: card-hk.yml
    INFO   [scheduling-1] 2021-09-12 21:33:55.009 c.k.s.s.FileDynamicRouteService - >>>>>>>>>> load file resource: pancake.yml
    DEBUG  [scheduling-1] 2021-09-12 21:33:55.009 o.s.boot.env.OriginTrackedYamlLoader - Loading from YAML: file [/home/un/code/springBoot_2017/spb-demo/spb-gateway/src/main/resources/pancake.yml]
    DEBUG  [scheduling-1] 2021-09-12 21:33:55.010 o.s.boot.env.OriginTrackedYamlLoader - Merging document (no matchers set): {routes=[{uri=http://pancake.${gateway.route.domain.postfix}, predicates=[Path=^/api/pancake/v1/*,^/api/pancake/v1/*/query]}, {predicates=[Path=/api/pancake/v1/coin/query], filters=[RewritePath=/api/pancake/v1/coin/query, /api/v1/coin/query]}]}
    DEBUG  [scheduling-1] 2021-09-12 21:33:55.011 o.s.boot.env.OriginTrackedYamlLoader - Loaded 1 document from YAML resource: file [/home/un/code/springBoot_2017/spb-demo/spb-gateway/src/main/resources/pancake.yml]
    INFO   [scheduling-1] 2021-09-12 21:33:55.011 c.k.s.s.FileDynamicRouteService - >>>>>>>>>> MutablePropertySources add propertySource: pancake.yml
    INFO   [scheduling-1] 2021-09-12 21:33:55.011 c.k.s.s.FileDynamicRouteService - >>>>>>>>>> load file resource: passport-hk.yml
    DEBUG  [scheduling-1] 2021-09-12 21:33:55.011 o.s.boot.env.OriginTrackedYamlLoader - Loading from YAML: file [/home/un/code/springBoot_2017/spb-demo/spb-gateway/src/main/resources/passport-hk.yml]
    DEBUG  [scheduling-1] 2021-09-12 21:33:55.013 o.s.boot.env.OriginTrackedYamlLoader - Merging document (no matchers set): {routes=[{uri=http://passport-hk.${gateway.route.domain.postfix}, predicates=[Path=/api/passport-hk/v1/passport/query], auths=[sms]}]}
    DEBUG  [scheduling-1] 2021-09-12 21:33:55.013 o.s.boot.env.OriginTrackedYamlLoader - Loaded 1 document from YAML resource: file [/home/un/code/springBoot_2017/spb-demo/spb-gateway/src/main/resources/passport-hk.yml]
    INFO   [scheduling-1] 2021-09-12 21:33:55.013 c.k.s.s.FileDynamicRouteService - >>>>>>>>>> MutablePropertySources add propertySource: passport-hk.yml
    INFO   [scheduling-1] 2021-09-12 21:33:55.013 c.k.s.s.FileDynamicRouteService - >>>>>>>>>> BrianGatewayProperties filename:card-hk.yml
    INFO   [scheduling-1] 2021-09-12 21:33:55.020 c.k.s.s.FileDynamicRouteService - >>>>>>>>>> BrianGatewayProperties(url=null, routes=[{id=null, uri=http://card-hk.free.beeceptor.com, predicates=[PredicateDefinition{name='Path', args={_genkey_0=/api/hk/card/v1/card/query}}, PredicateDefinition{name='Method', args={_genkey_0=POST}}], filters=[], metadata={}, order=0, apiKeys=[], auths=[]}, {id=null, uri=http://card-hk.free.beeceptor.com, predicates=[PredicateDefinition{name='Path', args={_genkey_0=/api/hk/card/v1/er/query}}, PredicateDefinition{name='Method', args={_genkey_0=POST}}], filters=[], metadata={}, order=0, apiKeys=[], auths=[]}])
    INFO   [scheduling-1] 2021-09-12 21:33:55.020 c.k.s.t.BrianRouteDefinitionTransformer - >>>>>>>>>> route path: /api/hk/card/v1/card/query
    INFO   [scheduling-1] 2021-09-12 21:33:55.020 c.k.s.t.BrianRouteDefinitionTransformer - >>>>>>>>>> route path: /api/hk/card/v1/er/query
    INFO   [scheduling-1] 2021-09-12 21:33:55.021 c.k.s.s.FileDynamicRouteService - >>>>>>>>>> BrianGatewayProperties filename:pancake.yml
    INFO   [scheduling-1] 2021-09-12 21:33:55.026 c.k.s.s.FileDynamicRouteService - >>>>>>>>>> BrianGatewayProperties(url=null, routes=[{id=null, uri=http://pancake.free.beeceptor.com, predicates=[PredicateDefinition{name='Path', args={_genkey_0=^/api/pancake/v1/*, _genkey_1=^/api/pancake/v1/*/query}}], filters=[], metadata={}, order=0, apiKeys=[], auths=[]}, {id=null, uri=null, predicates=[PredicateDefinition{name='Path', args={_genkey_0=/api/pancake/v1/coin/query}}], filters=[FilterDefinition{name='RewritePath', args={_genkey_0=/api/pancake/v1/coin/query, _genkey_1=/api/v1/coin/query}}], metadata={}, order=0, apiKeys=[], auths=[]}])
    INFO   [scheduling-1] 2021-09-12 21:33:55.026 c.k.s.t.BrianRouteDefinitionTransformer - >>>>>>>>>> route path: ^/api/pancake/v1/*
    INFO   [scheduling-1] 2021-09-12 21:33:55.026 c.k.s.t.BrianRouteDefinitionTransformer - >>>>>>>>>> route path: /api/pancake/v1/coin/query
    INFO   [scheduling-1] 2021-09-12 21:33:55.027 c.k.s.t.BrianRouteDefinitionTransformer - >>>>>>>>>>  redefine the path {/api/pancake/v1/coin/query} and new domain {https://pancake.free.beeceptor.com}
    INFO   [scheduling-1] 2021-09-12 21:33:55.027 c.k.s.s.FileDynamicRouteService - >>>>>>>>>> BrianGatewayProperties filename:passport-hk.yml
    INFO   [scheduling-1] 2021-09-12 21:33:55.032 c.k.s.s.FileDynamicRouteService - >>>>>>>>>> BrianGatewayProperties(url=null, routes=[{id=null, uri=http://passport-hk.free.beeceptor.com, predicates=[PredicateDefinition{name='Path', args={_genkey_0=/api/passport-hk/v1/passport/query}}], filters=[], metadata={}, order=0, apiKeys=[], auths=[sms]}])
    INFO   [scheduling-1] 2021-09-12 21:33:55.033 c.k.s.t.BrianRouteDefinitionTransformer - >>>>>>>>>> route path: /api/passport-hk/v1/passport/query
    INFO   [scheduling-1] 2021-09-12 21:33:55.033 c.k.s.t.BrianRouteDefinitionTransformer - >>>>>>>>>>  redefine the path {/api/passport-hk/v1/passport/query} and new domain {https://passport-hk.free.beeceptor.com}
    INFO   [scheduling-1] 2021-09-12 21:33:55.033 c.k.s.s.FileDynamicRouteService - >>>>>>>>>> add routeDefinition:{id=http://card-hk.free.beeceptor.com@-792698083, uri=http://card-hk.free.beeceptor.com, predicates=[PredicateDefinition{name='Path', args={_genkey_0=/api/hk/card/v1/card/query}}, PredicateDefinition{name='Method', args={_genkey_0=POST}}], filters=[FilterDefinition{name='BrianConfig', args={auths=[pwd, sms, gAuth], apiKeys=}}, FilterDefinition{name='RewritePath', args={regexp=^/api/(?<region>[a-zA-Z-]*)/(?<domain>[a-zA-Z-]*)/v(?<version>[0-9])/(?<path>.*), replacement=/v${version}/${path}}}], metadata={}, order=0, apiKeys=[], auths=[]}
    DEBUG  [scheduling-1] 2021-09-12 21:33:55.034 c.k.s.s.FileDynamicRouteService - >>>>>>>>>> remove the RouteDefinition id is: http://card-hk.free.beeceptor.com@1548203624
    INFO   [scheduling-1] 2021-09-12 21:33:55.034 c.k.s.s.FileDynamicRouteService - >>>>>>>>>> delete routeDefinition:{id=http://card-hk.free.beeceptor.com@1548203624, uri=http://card-hk.free.beeceptor.com, predicates=[PredicateDefinition{name='Path', args={_genkey_0=/api/hk/card/v1/uuu/query}}, PredicateDefinition{name='Method', args={_genkey_0=POST}}], filters=[FilterDefinition{name='BrianConfig', args={auths=[pwd, sms, gAuth], apiKeys=}}, FilterDefinition{name='RewritePath', args={regexp=^/api/(?<region>[a-zA-Z-]*)/(?<domain>[a-zA-Z-]*)/v(?<version>[0-9])/(?<path>.*), replacement=/v${version}/${path}}}], metadata={}, order=0, apiKeys=[], auths=[]}
    INFO   [scheduling-1] 2021-09-12 21:33:55.034 c.k.s.s.FileRefreshRouteService - )))))))))))))))))))))))))))))) FileRefreshRouteService refreshRoute~~~
    INFO   [scheduling-1] 2021-09-12 21:33:55.034 c.k.s.s.FileDynamicRouteService - )))))))))))))))))))))))))))))) FileDynamicRouteService getRouteDefinitions~~~
    DEBUG  [scheduling-1] 2021-09-12 21:33:55.034 o.s.c.g.r.RouteDefinitionRouteLocator - RouteDefinition http://passport-hk.free.beeceptor.com@1466789461 applying {_genkey_0=/api/passport-hk/v1/passport/query} to Path
    DEBUG  [scheduling-1] 2021-09-12 21:33:55.035 o.s.c.g.r.RouteDefinitionRouteLocator - RouteDefinition http://passport-hk.free.beeceptor.com@1466789461 applying filter {auths=[sms], apiKeys=} to BrianConfig
    DEBUG  [scheduling-1] 2021-09-12 21:33:55.036 o.s.c.g.r.RouteDefinitionRouteLocator - RouteDefinition http://passport-hk.free.beeceptor.com@1466789461 applying filter {regexp=^/api/(?<domain>[a-zA-Z-]*)/v(?<version>[0-9])/(?<path>.*), replacement=/v${version}/${path}} to RewritePath
    DEBUG  [scheduling-1] 2021-09-12 21:33:55.037 o.s.c.g.r.RouteDefinitionRouteLocator - RouteDefinition matched: http://passport-hk.free.beeceptor.com@1466789461
    DEBUG  [scheduling-1] 2021-09-12 21:33:55.037 o.s.c.g.r.RouteDefinitionRouteLocator - RouteDefinition http://pancake.free.beeceptor.com@-1468813552 applying {_genkey_0=^/api/pancake/v1/*, _genkey_1=^/api/pancake/v1/*/query} to Path
    DEBUG  [scheduling-1] 2021-09-12 21:33:55.038 o.s.c.g.r.RouteDefinitionRouteLocator - RouteDefinition http://pancake.free.beeceptor.com@-1468813552 applying filter {auths=[pwd, sms, gAuth], apiKeys=} to BrianConfig
    DEBUG  [scheduling-1] 2021-09-12 21:33:55.039 o.s.c.g.r.RouteDefinitionRouteLocator - RouteDefinition matched: http://pancake.free.beeceptor.com@-1468813552
    DEBUG  [scheduling-1] 2021-09-12 21:33:55.040 o.s.c.g.r.RouteDefinitionRouteLocator - RouteDefinition http://card-hk.free.beeceptor.com@-792698083 applying {_genkey_0=/api/hk/card/v1/card/query} to Path
    DEBUG  [scheduling-1] 2021-09-12 21:33:55.041 o.s.c.g.r.RouteDefinitionRouteLocator - RouteDefinition http://card-hk.free.beeceptor.com@-792698083 applying {_genkey_0=POST} to Method
    DEBUG  [scheduling-1] 2021-09-12 21:33:55.041 o.s.c.g.r.RouteDefinitionRouteLocator - RouteDefinition http://card-hk.free.beeceptor.com@-792698083 applying filter {auths=[pwd, sms, gAuth], apiKeys=} to BrianConfig
    DEBUG  [scheduling-1] 2021-09-12 21:33:55.042 o.s.c.g.r.RouteDefinitionRouteLocator - RouteDefinition http://card-hk.free.beeceptor.com@-792698083 applying filter {regexp=^/api/(?<region>[a-zA-Z-]*)/(?<domain>[a-zA-Z-]*)/v(?<version>[0-9])/(?<path>.*), replacement=/v${version}/${path}} to RewritePath
    DEBUG  [scheduling-1] 2021-09-12 21:33:55.043 o.s.c.g.r.RouteDefinitionRouteLocator - RouteDefinition matched: http://card-hk.free.beeceptor.com@-792698083
    DEBUG  [scheduling-1] 2021-09-12 21:33:55.043 o.s.c.g.r.RouteDefinitionRouteLocator - RouteDefinition https://pancake.free.beeceptor.com@1912448187 applying {_genkey_0=/api/pancake/v1/coin/query} to Path
    DEBUG  [scheduling-1] 2021-09-12 21:33:55.044 o.s.c.g.r.RouteDefinitionRouteLocator - RouteDefinition https://pancake.free.beeceptor.com@1912448187 applying filter {_genkey_0=/api/pancake/v1/coin/query, _genkey_1=/api/v1/coin/query} to RewritePath
    DEBUG  [scheduling-1] 2021-09-12 21:33:55.045 o.s.c.g.r.RouteDefinitionRouteLocator - RouteDefinition https://pancake.free.beeceptor.com@1912448187 applying filter {auths=[pwd, sms, gAuth], apiKeys=} to BrianConfig
    DEBUG  [scheduling-1] 2021-09-12 21:33:55.046 o.s.c.g.r.RouteDefinitionRouteLocator - RouteDefinition matched: https://pancake.free.beeceptor.com@1912448187
    DEBUG  [scheduling-1] 2021-09-12 21:33:55.046 o.s.c.g.r.RouteDefinitionRouteLocator - RouteDefinition http://card-hk.free.beeceptor.com@-679151078 applying {_genkey_0=/api/hk/card/v1/er/query} to Path
    DEBUG  [scheduling-1] 2021-09-12 21:33:55.047 o.s.c.g.r.RouteDefinitionRouteLocator - RouteDefinition http://card-hk.free.beeceptor.com@-679151078 applying {_genkey_0=POST} to Method
    DEBUG  [scheduling-1] 2021-09-12 21:33:55.047 o.s.c.g.r.RouteDefinitionRouteLocator - RouteDefinition http://card-hk.free.beeceptor.com@-679151078 applying filter {auths=[pwd, sms, gAuth], apiKeys=} to BrianConfig
    DEBUG  [scheduling-1] 2021-09-12 21:33:55.048 o.s.c.g.r.RouteDefinitionRouteLocator - RouteDefinition http://card-hk.free.beeceptor.com@-679151078 applying filter {regexp=^/api/(?<region>[a-zA-Z-]*)/(?<domain>[a-zA-Z-]*)/v(?<version>[0-9])/(?<path>.*), replacement=/v${version}/${path}} to RewritePath
    DEBUG  [scheduling-1] 2021-09-12 21:33:55.049 o.s.c.g.r.RouteDefinitionRouteLocator - RouteDefinition matched: http://card-hk.free.beeceptor.com@-679151078

    再次通过actuator的接口查看,可以看到路由的Path已经修改了,/api/hk/card/v1/uuu/query -> /api/hk/card/v1/card/query

     

    到此四类动态路由的实现方式,都介绍完毕了~~~

    顺便推荐一个免费好用的Mock Server: https://beeceptor.com/

  • 相关阅读:
    DBA常用脚本 二、性能监控
    ORA01012:not logged on的解决办法
    线性布局LinearLayout
    Android Handler.postDelayed()方法
    java String.valueOf()
    布局管理器ViewGroup
    UE3的数据绑定笔记
    概念、实现和沟通
    DirectX11的Shader Reflect的几个问题(2012222更新)
    测试livewriter
  • 原文地址:https://www.cnblogs.com/hlkawa/p/14931961.html
Copyright © 2020-2023  润新知