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

1.基于Nacos的动态路由

Nacos官方简介

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

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

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

1.1 相关版本如下

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

1.2 实现思路

ok,上代码

properties配置

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

NacosGatewayConfig配置类

package com.kawa.spbgateway.config;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; @Configuration
public class NacosGatewayConfig {
public static final long DEFAULT_TIMEOUT = 30000; public static String NACOS_SERVER_ADDR; public static String NACOS_NAMESPACE; public static String NACOS_ROUTE_DATA_ID; public static String NACOS_ROUTE_GROUP; @Value("${spring.cloud.nacos.discovery.server-addr}")
public void setNacosServerAddr(String nacosServerAddr) {
NACOS_SERVER_ADDR = nacosServerAddr;
} @Value("${spring.cloud.nacos.discovery.namespace}")
public void setNacosNamespace(String nacosNamespace) {
NACOS_NAMESPACE = nacosNamespace;
} @Value("${nacos.gateway.route.config.data-id}")
public void setNacosRouteDataId(String nacosRouteDataId) {
NACOS_ROUTE_DATA_ID = nacosRouteDataId;
} @Value("${nacos.gateway.route.config.group}")
public void setNacosRouteGroup(String nacosRouteGroup) {
NACOS_ROUTE_GROUP = nacosRouteGroup;
} @Bean
public ObjectMapper getObjectMapper() {
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
return objectMapper;
}
}

NacosDynamicRouteService类

加载和监听路由

package com.kawa.spbgateway.service;

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

NacosRefreshRouteService类 

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

package com.kawa.spbgateway.service;

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

测试一下

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

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

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

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

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

2.1 相关配置

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

2.2  实现思路

上代码

proerties配置

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

RedisConfiguration类

package com.kawa.spbgateway.config;

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

RedisDynamicRouteService类

操作redis的类

package com.kawa.spbgateway.service;

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

RedisRefreshRouteService类

动态刷新路由

package com.kawa.spbgateway.service;

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

RedisDynamicRouteController类

package com.kawa.spbgateway.controller;

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

ok,测试下

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

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

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

ok,测试下路由是否生效

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

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

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

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

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

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

3.1 相关配置和接口

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

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

3.2 实现思路

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

测试一下

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

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

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

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

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

4.1 实现思路

上代码

route配置yml

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

card-hk.yml

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

pancake.yml

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

passport-hk.yml

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

FileRefreshRouteService类

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

package com.kawa.spbgateway.service;

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

FileDynamicRouteService类

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

package com.kawa.spbgateway.service;

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

BrianRouteDefinition类 

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

package com.kawa.spbgateway.route;

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

BrianConfigGatewayFilterFactory类

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

package com.kawa.spbgateway.filter;

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

RefreshRoutePropertySource类

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

package com.kawa.spbgateway.property;

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

BrianGatewayProperties类

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

package com.kawa.spbgateway.domain;

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

BrianRouteDefinitionTransformer类

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

package com.kawa.spbgateway.transformer;

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

ok,测试下,启动项目

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

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

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

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

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

日志也打印相关日志 

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

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

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

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

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

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

springcloud3(五) spring cloud gateway动态路由的四类实现方式的更多相关文章

  1. 通过Nacos动态刷新Spring Cloud Gateway的路由

    通过Nacos动态刷新Spring Cloud Gateway的路由 一.背景 二.解决方案 三.实现功能 四.实现步骤 1.网关服务的实现 1.pom文件 2.bootstrap.yml配置文件 3 ...

  2. Spring Cloud Gateway 动态修改请求参数解决 # URL 编码错误传参问题

    Spring Cloud Gateway 动态修改请求参数解决 # URL 编码错误传参问题 继实现动态修改请求 Body 以及重试带 Body 的请求之后,我们又遇到了一个小问题.最近很多接口,收到 ...

  3. Spring Cloud Alibaba学习笔记(17) - Spring Cloud Gateway 自定义路由谓词工厂

    在前文中,我们介绍了Spring Cloud Gateway内置了一系列的路由谓词工厂,但是如果这些内置的路由谓词工厂不能满足业务需求的话,我们可以自定义路由谓词工厂来实现特定的需求. 例如有某个服务 ...

  4. spring cloud gateway网关路由分配

    1, 基于父工程,新建一个模块 2,pom文件添加依赖 <dependencies> <dependency> <groupId>org.springframewo ...

  5. Spring Cloud Gateway服务网关

    原文:https://www.cnblogs.com/ityouknow/p/10141740.html Spring 官方最终还是按捺不住推出了自己的网关组件:Spring Cloud Gatewa ...

  6. 网关服务Spring Cloud Gateway(一)

    Spring 官方最终还是按捺不住推出了自己的网关组件:Spring Cloud Gateway ,相比之前我们使用的 Zuul(1.x) 它有哪些优势呢?Zuul(1.x) 基于 Servlet,使 ...

  7. 跟我学SpringCloud | 第十二篇:Spring Cloud Gateway初探

    SpringCloud系列教程 | 第十二篇:Spring Cloud Gateway初探 Springboot: 2.1.6.RELEASE SpringCloud: Greenwich.SR1 如 ...

  8. Spring Cloud Gateway 服务网关快速上手

    Spring Cloud Gateway 服务网关 API 主流网关有NGINX.ZUUL.Spring Cloud Gateway.Linkerd等:Spring Cloud Gateway构建于 ...

  9. spring cloud:服务网关 Spring Cloud GateWay 入门

    Spring 官方最终还是按捺不住推出了自己的网关组件:Spring Cloud Gateway ,相比之前我们使用的 Zuul(1.x) 它有哪些优势呢?Zuul(1.x) 基于 Servlet,使 ...

随机推荐

  1. JUC学习笔记(四)

    JUC学习笔记(一)https://www.cnblogs.com/lm66/p/15118407.html JUC学习笔记(二)https://www.cnblogs.com/lm66/p/1511 ...

  2. Wireshark基础

    Wireshark基础 wireshark简介: wireshark是一款用于追踪网络流量的辅助工具,帮助捕获.分析网络封包,并进行分析. 主要功能:     1.网络分析任务         查看网 ...

  3. 华为eNSP基础入门-配置SSH远程登录

    eNSP 华为模拟器ensp (Enterprise Network Simulation Platform) 是华为官方推出的一款强大的图形化网络仿真工具平台,主要对企业网路由器.交换机.WLAN等 ...

  4. CentOS文件目录类语法

    目录 一.目录查看切换类 1. pwd 显示当前工作目录的绝对路径 2. ls 列出目录的内容 二.文件与目录创建删除类 1. mkdir 创建一个新目录 2. touch 创建空文件 3. rmdi ...

  5. Python函数调用中的值传递和引用传递问题

    这一问题O' Reilly出版的"Learning Python" 2nd Edition的 13.4 节有专门论述,对于不可变(immutabe)对象,函数参数(这里是x和y)只 ...

  6. SpringCloud升级之路2020.0.x版-14.UnderTow AccessLog 配置介绍

    本系列代码地址:https://github.com/HashZhang/spring-cloud-scaffold/tree/master/spring-cloud-iiford server: u ...

  7. CVE-2021-21978 VMware View Planner 远程代码执行漏洞通告 | 附 POC

    漏洞简介 VMware 是一家云基础架构和移动商务解决方案厂商,View Planner 是他旗下推出的一款针对view桌面的测试工具.2021年03月02日,VMware 官方披露了 CVE-202 ...

  8. SQL注入的那些面试题总结

    一.知识储备类 1.SQL与NoSQL的区别? SQL:关系型数据库 NoSQL:非关系型数据库 存储方式:SQL具有特定的结构表,NoSQL存储方式灵活 性能:NoSQL较优于SQL 数据类型:SQ ...

  9. flutter中存储键值对简单数据(相当于前端localstorage概念)

    首先需要安装一个官方推荐包: 1 dependencies: 2 flutter: 3 sdk: flutter 4 shared_preferences: any // 先获取 shared pre ...

  10. RabbitMQ 的使用

    MiaoshaMessage  类 ---------------------------------------------------------------- import com.imooc. ...