写这篇博客主要是为了汇总下动态路由的多种实现方式,没有好坏之分,任何的方案都是依赖业务场景需求的,现在网上实现方式主要有: 基于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;}@Beanpublic 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 {@Autowiredprivate NacosRefreshRouteService nacosRefreshRouteService;private ConfigService configService;@Autowiredprivate ObjectMapper objectMapper;@PostConstructpublic 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() {@Overridepublic Executor getExecutor() {log.info("-------------------getExecutor-------------------");return null;}@Overridepublic 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;@Autowiredprivate RouteDefinitionWriter routeDefinitionWriter;@Autowiredprivate RouteDefinitionLocator routeDefinitionLocator;private List<String> routeIds = new ArrayList<>();@Overridepublic 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);// 删除缓存routerDefinitionList<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 {@Beanpublic 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:";@Autowiredprivate StringRedisTemplate redisTemplate;@Autowiredprivate 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 {@Autowiredprivate RedisDynamicRouteService repository;@Autowiredprivate RouteDefinitionWriter routeDefinitionWriter;private ApplicationEventPublisher publisher;@Overridepublic 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));}@Overridepublic 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 {@Autowiredprivate 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/queryfilters:- 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/queryauths:- 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 {@Autowiredprivate FileDynamicRouteService routeService;@Autowiredprivate ApplicationEventPublisher applicationEventPublisher;@Overridepublic 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);}}@Overridepublic 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 RouteDefinitionif (CollectionUtils.isEmpty(routes)) {targets.forEach(rd -> {// add routeDefinitionsave(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 RouteDefinitionsave(Mono.just(rd)).subscribe();log.info(">>>>>>>>>> add routeDefinition:{}", rd);}// not null don't updateif (Objects.nonNull(routes.get(rd.getId())) && rd.equals(routes.get(rd.getId()))) {definitions.remove(rd);}});// remove RouteDefinitionif (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;}@Overridepublic Flux<RouteDefinition> getRouteDefinitions() {log.info(")))))))))))))))))))))))))))))) FileDynamicRouteService getRouteDefinitions~~~");return Flux.fromIterable(routes.values());}@Overridepublic Mono<Void> save(Mono<RouteDefinition> route) {return route.flatMap(r -> {routes.put(r.getId(), r);return Mono.empty();});}@Overridepublic 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<>();@Overridepublic int hashCode() {return Objects.hash(getId(), getPredicates(), getFilters(), getUri(), getMetadata(), getOrder(),this.apiKeys, this.auths);}@Overridepublic 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);}@Overridepublic 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;}@Overridepublic 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;}@Overridepublic 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);}@Overridepublic 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 ConfigGatewayFilterFilterDefinition 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 idsetRouteId(brianRouteDefinition);}long count = brianRouteDefinition.getFilters().stream().filter(filterDefinition -> filterDefinition.getName().equals(REWRITE_GATEWAY_FILTER_CLASS_NAME)).count();// get path value from Prediction configvar 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 idsetRouteId(brianRouteDefinition);}// add RewritePathGatewayFilterif (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 idsetRouteId(brianRouteDefinition);}// add RewritePathGatewayFilterif (count < 1L) {addRewriteFilter(brianRouteDefinition, extendRewritePathRegexp, extendRewritePathReplacement);}return brianRouteDefinition;}if (Objects.isNull(brianRouteDefinition.getUri())) {brianRouteDefinition.setUri(URI.create(FALL_BACK_URI + path));// set route idsetRouteId(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

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

来源 http://t.zoukankan.com/hlkawa-p-14931961.html

记得点「」和「在看」↓

爱你们

springgateway动态路由的四类实现方式相关推荐

  1. Spring Cloud Alibaba 集成 Gateway 实现动态路由功能

    文章目录 1 摘要 2 核心 Maven 依赖 3 名词释义 4 Gateway 动态路由原理 5 数据库表 6 核心代码 6.1 配置信息 6.2 路由实体类 6.3 本地路由数据库持久层(DAO/ ...

  2. 查看动态代理生成的类文件

    JDK的动态代理 场景 在数据保存前后加入日志 实例 先定义数据保存接口 interface DataSave{void save(); } 然后定义一个sql拦截器,在数据保存前后打印日志 clas ...

  3. 第二百六十四节,Tornado框架-基于正则的动态路由映射分页数据获取计算

    Tornado框架-基于正则的动态路由映射分页数据获取计算 分页基本显示数据 第一步.设置正则路由映射配置,(r"/index/(?P<page>\d*)", inde ...

  4. 计算机网络实验(华为eNSP模拟器)——第四章 配置静态路由、动态路由

    目录 前言 一.关闭泛洪信息 二.静态路由 命令 例题 三.动态路由 (一)RIP协议 RIP命令 例题 (二)OSPF协议 OSPF命令 例题(单区域) 例题(多区域) 四.查看全局路由表 结语 前 ...

  5. 详解SpringCloud-gateway动态路由两种方式,以及路由加载过程

    gateway配置路由主要有两种方式,一种是用yml配置文件,一种是写代码里,这两种方式都是不支持动态配置的.如: 下面就来看看gateway是如何加载这些配置信息的. 1 路由初始化 无论是yml还 ...

  6. Vue实现动态路由的两种方式总结

    实现动态路由有两种方式,一种是后端返回什么,前端就展示什么,另一种是后端只返回角色,前端根据角色拼接数据信息展示.相比第一种方式,第二种方式在企业中更常用. 第一种方式: (一)后端需返回类似Vue- ...

  7. Angular中使用JS实现路由跳转、动态路由传值、get方式传值

    场景 Angular介绍.安装Angular Cli.创建Angular项目入门教程: https://blog.csdn.net/BADAO_LIUMANG_QIZHI/article/detail ...

  8. tp5类的属性不存在_thinkPHP5.1框架中Request类四种调用方式示例

    本文实例讲述了thinkPHP5.1框架中Request类四种调用方式.分享给大家供大家参考,具体如下: 1. 传统调用 访问方式:http://127.0.0.1/demo/demo3/test?n ...

  9. vue动态路由变化页面不重新加载问题,实现方式比较简单,就是有点鸡贼

    由于使用了动态路由,有时候页面需根据路由来动态变化,整个页面需要重新加载,但是地址指向同一个,这边用了一个比较取巧的方式 将整个页面封装成一个子组件 children => formTe,参数可 ...

最新文章

  1. chrome弱网_请你进行一下弱网模拟
  2. Inline Hook
  3. 【数据处理】python数据清洗通用手法:缺失值处理
  4. 无锡技师学院计算机系,无锡技师学院
  5. ITK:创建一个二进制球结构元素
  6. 对mysql优化关注_MySQL优化看这篇就对了
  7. Mybatis传入参数map,读取map原创
  8. python的std函数_Python numpy.nanstd函数方法的使用
  9. WPF DataGrid 通过自定义表头模拟首行固定
  10. 数据结构设计_合并多种疾病,如何设计数据结构?
  11. Rust——Macos安装使用
  12. web前端最全各类资源
  13. 《天天数学》连载36:二月五日
  14. 11 个问题,帮你彻底搞懂工业互联网
  15. 实验题目用正交多项式做小二乘曲线拟合
  16. java设计模型 解析工厂模式、proxy-agent模式、templete模式
  17. duilib开发基础:创建自定义控件的过程
  18. SecureCRT下载和破解
  19. 一阶自相关系数 matlab,Eviews求一阶自相关系数
  20. Android系统中SD卡各文件夹名称功能详解

热门文章

  1. 手游联运系统海外版丨游戏联运系统海外版丨海外游戏联运系统丨海外版游戏SDK
  2. startActivityForResult被标记为弃用后,如何优雅的启动Activity?
  3. 一张纸厚度是多少毫米_一张纸的厚度是多少?
  4. 安卓读取蓝牙BLE设备信息
  5. 本地生活综合性需求图谱的构建及应用
  6. 大一大二的计算机专业同学应该学习什么?如何学习?
  7. QT控件 — QScrollArea
  8. A Game of Thrones(62)
  9. 来,了解一下90年代的网站
  10. 2019年猪年海报PSD模板-第三部分