• 微信公众号:美女很有趣。 工作之余,放松一下,关注即送10G+美女照片!

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

开发技术 开发技术 4小时前 3次浏览

写这篇博客主要是为了汇总下动态路由的多种实现方式,没有好坏之分,任何的方案都是依赖业务场景需求的,现在网上实现方式主要有: 基于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 实现思路

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

 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:rn{}", 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:nt{}", 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”要和配置一一对应

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

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

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

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

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

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

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

2.1 相关配置

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

2.2  实现思路

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

上代码

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

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

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

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

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

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

ok,测试下路由是否生效

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

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

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

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

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

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

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

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

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

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))

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

测试一下

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

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

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

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

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

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

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

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

4.1 实现思路

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

上代码

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的接口查看,可以看到路由已经生效了

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

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

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

日志也打印相关日志 springcloud3(五) spring cloud gateway动态路由的四类实现方式

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

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

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

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

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

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

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


喜欢 (0)