寫這篇部落格主要是為了彙總下動態路由的多種實現方式,沒有好壞之分,任何的方案都是依賴業務場景需求的,現在網上實現方式主要有: 基於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/