springgateway动态路由的四类实现方式

JAVA乐园

共 15564字,需浏览 32分钟

 ·

2022-05-28 17:18


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

1.2 实现思路

 ok,上代码

properties配置

### nacos configuration startspring.cloud.nacos.discovery.server-addr=127.0.0.1:8848spring.cloud.nacos.discovery.namespace=50f5dcf0-f3c0-4c79-9715-0e25e3959ssdnacos.gateway.route.config.data-id=server-routesnacos.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;
@Configurationpublic class NacosGatewayConfig { public static final long DEFAULT_TIMEOUT = 30000;
public static String NACOS_SERVER_ADDR;
public static String NACOS_NAMESPACE;
public static String NACOS_ROUTE_DATA_ID;
public static String NACOS_ROUTE_GROUP;
@Value("${spring.cloud.nacos.discovery.server-addr}") public void setNacosServerAddr(String nacosServerAddr) { NACOS_SERVER_ADDR = nacosServerAddr; }
@Value("${spring.cloud.nacos.discovery.namespace}") public void setNacosNamespace(String nacosNamespace) { NACOS_NAMESPACE = nacosNamespace; }
@Value("${nacos.gateway.route.config.data-id}") public void setNacosRouteDataId(String nacosRouteDataId) { NACOS_ROUTE_DATA_ID = nacosRouteDataId; }
@Value("${nacos.gateway.route.config.group}") public void setNacosRouteGroup(String nacosRouteGroup) { NACOS_ROUTE_GROUP = nacosRouteGroup; }
@Bean public ObjectMapper getObjectMapper() { ObjectMapper objectMapper = new ObjectMapper(); objectMapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false); return objectMapper; }}

NacosDynamicRouteService类

加载和监听路由

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

NacosRefreshRouteService类 

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

package com.kawa.spbgateway.service;
import com.kawa.spbgateway.route.CustomizedRouteDefinition;import lombok.extern.slf4j.Slf4j;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.cloud.gateway.event.RefreshRoutesEvent;import org.springframework.cloud.gateway.route.RouteDefinition;import org.springframework.cloud.gateway.route.RouteDefinitionLocator;import org.springframework.cloud.gateway.route.RouteDefinitionWriter;import org.springframework.context.ApplicationEventPublisher;import org.springframework.context.ApplicationEventPublisherAware;import org.springframework.stereotype.Service;import org.springframework.util.CollectionUtils;import reactor.core.publisher.Mono;
import java.util.ArrayList;import java.util.List;

@Service@Slf4jpublic class NacosRefreshRouteService implements ApplicationEventPublisherAware {
private ApplicationEventPublisher publisher;
@Autowired private RouteDefinitionWriter routeDefinitionWriter;
@Autowired private RouteDefinitionLocator routeDefinitionLocator;
private List 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 definitions) { log.info(">>>>>>>>>> gateway update route {}", definitions); // 删除缓存routerDefinition List 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.bootgroupId>    <artifactId>spring-boot-starter-data-redisartifactId>dependency>

2.2  实现思路

上代码

proerties配置

### redis configuration startspring.redis.database=0spring.redis.host=127.0.0.1spring.redis.port=10619spring.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;
@Configurationpublic 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@Servicepublic 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 getRouteDefinitions() { log.info(">>>>>>>>>> getRouteDefinitions <<<<<<<<<<"); List 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 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 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 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@Servicepublic 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 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> create(@RequestBody RouteDefinition entity) { dynamicRouteService.add(entity); return Mono.just(new ResponseEntity<>("save success", HttpStatus.OK)); }
@PostMapping("/update") public Mono> 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.bootgroupId>     <artifactId>spring-boot-starter-actuatorartifactId>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@Servicepublic 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@Slf4jpublic class FileDynamicRouteService implements RouteDefinitionRepository {
private Environment environment;
private String folder;
private List resourceFileExt;
private ConcurrentHashMap fileChecksumMap = new ConcurrentHashMap<>(32);
private ConcurrentHashMap routes = new ConcurrentHashMap<>(32);
private BrianRouteDefinitionTransformer transformer = new BrianRouteDefinitionTransformer();
public FileDynamicRouteService(Environment environment) { this.environment = environment; }
public boolean refreshRoutes() throws IOException { getAndInitProperties(); List 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 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 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 readRouteConfig(List resources) { Binder binder = Binder.get(environment); List 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 resources) { if (environment instanceof ConfigurableEnvironment) { MutablePropertySources propertySources = ((ConfigurableEnvironment) this.environment).getPropertySources();
List 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 propertySourceLoaders) { propertySourceLoaders.forEach(psl -> { List fileExts = Arrays.asList(psl.getFileExtensions()); String filename = resource.getFilename(); if (fileExts.contains(StringUtils.getFilenameExtension(filename))) { log.info(">>>>>>>>>> load file resource: {}", filename); try { List> 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 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 getCustomizedConfigs() { List resources = new ArrayList<>(); List exclude = Arrays.asList(EXCLUDES); try (DirectoryStream 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 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 getRouteDefinitions() { log.info(")))))))))))))))))))))))))))))) FileDynamicRouteService getRouteDefinitions~~~"); return Flux.fromIterable(routes.values()); }
@Override public Mono save(Mono route) { return route.flatMap(r -> { routes.put(r.getId(), r); return Mono.empty(); }); }
@Override public Mono delete(Mono 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@Datapublic class BrianRouteDefinition extends RouteDefinition { private List apiKeys = new ArrayList<>(); private List 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@Componentpublic 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 apiKeys;
public String[] getAuths() { return auths; }
public void setAuths(String[] auths) { this.auths = auths; }
public List getApiKeys() { return apiKeys; }
public void setApiKeys(List 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;
@Datapublic 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.*;
@Slf4jpublic class BrianRouteDefinitionTransformer { private String defaultRewritePathRegexp = "^/api/(?[a-zA-Z-]*)/v(?[0-9])/(?.*)"; private String defaultRewritePathReplacement = "/v$\{version}/$\{path}"; private String extendRewritePathRegexp = "^/api/(?[a-zA-Z-]*)/(?[a-zA-Z-]*)/v(?[0-9])/(?.*)"; private String extendRewritePathReplacement = "/v$\{version}/$\{path}";
private String defaultRewriteDomainRegexp = "^/api/(?[a-zA-Z-]*)/v.+/.*"; private String defaultRewriteDomainReplacement = "https://$\{domain}.free.beeceptor.com"; private String extendRewriteDomainRegexp = "^/api/(?[a-zA-Z-]*)/(?[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 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.ymlDEBUG  [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.ymlDEBUG  [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.ymlINFO   [restartedMain] 2021-09-12 21:11:11.463 c.k.s.s.FileDynamicRouteService - >>>>>>>>>> fileName:card-hk.yml,checksum:3867921742INFO   [restartedMain] 2021-09-12 21:11:11.463 c.k.s.s.FileDynamicRouteService - >>>>>>>>>> fileName:pancake.yml,checksum:2400413005INFO   [restartedMain] 2021-09-12 21:11:11.464 c.k.s.s.FileDynamicRouteService - >>>>>>>>>> fileName:passport-hk.yml,checksum:140450225INFO   [restartedMain] 2021-09-12 21:11:11.465 c.k.s.s.FileDynamicRouteService - >>>>>>>>>> load file resource: card-hk.ymlDEBUG  [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.ymlINFO   [restartedMain] 2021-09-12 21:11:11.516 c.k.s.s.FileDynamicRouteService - >>>>>>>>>> load file resource: pancake.ymlDEBUG  [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.ymlINFO   [restartedMain] 2021-09-12 21:11:11.518 c.k.s.s.FileDynamicRouteService - >>>>>>>>>> load file resource: passport-hk.ymlDEBUG  [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.ymlINFO   [restartedMain] 2021-09-12 21:11:11.522 c.k.s.s.FileDynamicRouteService - >>>>>>>>>> BrianGatewayProperties filename:card-hk.ymlINFO   [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/queryINFO   [restartedMain] 2021-09-12 21:11:11.554 c.k.s.t.BrianRouteDefinitionTransformer - >>>>>>>>>> route path: /api/hk/card/v1/er/queryINFO   [restartedMain] 2021-09-12 21:11:11.554 c.k.s.s.FileDynamicRouteService - >>>>>>>>>> BrianGatewayProperties filename:pancake.ymlINFO   [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/queryINFO   [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.ymlINFO   [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/queryINFO   [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/(?[a-zA-Z-]*)/(?[a-zA-Z-]*)/v(?[0-9])/(?.*), 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/(?[a-zA-Z-]*)/(?[a-zA-Z-]*)/v(?[0-9])/(?.*), 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/(?[a-zA-Z-]*)/v(?[0-9])/(?.*), 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 PathDEBUG  [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 MethodDEBUG  [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 BrianConfigDEBUG  [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/(?[a-zA-Z-]*)/(?[a-zA-Z-]*)/v(?[0-9])/(?.*), replacement=/v${version}/${path}} to RewritePathDEBUG  [restartedMain] 2021-09-12 21:11:11.679 o.s.c.g.r.RouteDefinitionRouteLocator - RouteDefinition matched: http://card-hk.free.beeceptor.com@1548203624DEBUG  [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 PathDEBUG  [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 BrianConfigDEBUG  [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/(?[a-zA-Z-]*)/v(?[0-9])/(?.*), replacement=/v${version}/${path}} to RewritePathDEBUG  [restartedMain] 2021-09-12 21:11:11.683 o.s.c.g.r.RouteDefinitionRouteLocator - RouteDefinition matched: http://passport-hk.free.beeceptor.com@1466789461DEBUG  [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 PathDEBUG  [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 BrianConfigDEBUG  [restartedMain] 2021-09-12 21:11:11.688 o.s.c.g.r.RouteDefinitionRouteLocator - RouteDefinition matched: http://pancake.free.beeceptor.com@-1468813552DEBUG  [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 PathDEBUG  [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 RewritePathDEBUG  [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 BrianConfigDEBUG  [restartedMain] 2021-09-12 21:11:11.693 o.s.c.g.r.RouteDefinitionRouteLocator - RouteDefinition matched: https://pancake.free.beeceptor.com@1912448187DEBUG  [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 PathDEBUG  [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 MethodDEBUG  [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 BrianConfigDEBUG  [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/(?[a-zA-Z-]*)/(?[a-zA-Z-]*)/v(?[0-9])/(?.*), replacement=/v${version}/${path}} to RewritePathDEBUG  [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.ymlDEBUG  [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.ymlDEBUG  [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.ymlINFO   [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.ymlDEBUG  [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.ymlDEBUG  [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.ymlINFO   [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.ymlDEBUG  [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.ymlDEBUG  [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.ymlINFO   [scheduling-1] 2021-09-12 21:33:55.004 c.k.s.s.FileDynamicRouteService - >>>>>>>>>> fileName:card-hk.yml,checksum:56354776INFO   [scheduling-1] 2021-09-12 21:33:55.004 c.k.s.s.FileDynamicRouteService - >>>>>>>>>> fileName:pancake.yml,checksum:2400413005INFO   [scheduling-1] 2021-09-12 21:33:55.004 c.k.s.s.FileDynamicRouteService - >>>>>>>>>> fileName:passport-hk.yml,checksum:1148328829INFO   [scheduling-1] 2021-09-12 21:33:55.004 c.k.s.s.FileDynamicRouteService - >>>>>>>>>> load file resource: card-hk.ymlDEBUG  [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.ymlINFO   [scheduling-1] 2021-09-12 21:33:55.009 c.k.s.s.FileDynamicRouteService - >>>>>>>>>> load file resource: pancake.ymlDEBUG  [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.ymlINFO   [scheduling-1] 2021-09-12 21:33:55.011 c.k.s.s.FileDynamicRouteService - >>>>>>>>>> load file resource: passport-hk.ymlDEBUG  [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.ymlINFO   [scheduling-1] 2021-09-12 21:33:55.013 c.k.s.s.FileDynamicRouteService - >>>>>>>>>> BrianGatewayProperties filename:card-hk.ymlINFO   [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/queryINFO   [scheduling-1] 2021-09-12 21:33:55.020 c.k.s.t.BrianRouteDefinitionTransformer - >>>>>>>>>> route path: /api/hk/card/v1/er/queryINFO   [scheduling-1] 2021-09-12 21:33:55.021 c.k.s.s.FileDynamicRouteService - >>>>>>>>>> BrianGatewayProperties filename:pancake.ymlINFO   [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/queryINFO   [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.ymlINFO   [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/queryINFO   [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/(?[a-zA-Z-]*)/(?[a-zA-Z-]*)/v(?[0-9])/(?.*), 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@1548203624INFO   [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/(?[a-zA-Z-]*)/(?[a-zA-Z-]*)/v(?[0-9])/(?.*), 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 PathDEBUG  [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 BrianConfigDEBUG  [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/(?[a-zA-Z-]*)/v(?[0-9])/(?.*), replacement=/v${version}/${path}} to RewritePathDEBUG  [scheduling-1] 2021-09-12 21:33:55.037 o.s.c.g.r.RouteDefinitionRouteLocator - RouteDefinition matched: http://passport-hk.free.beeceptor.com@1466789461DEBUG  [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 PathDEBUG  [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 BrianConfigDEBUG  [scheduling-1] 2021-09-12 21:33:55.039 o.s.c.g.r.RouteDefinitionRouteLocator - RouteDefinition matched: http://pancake.free.beeceptor.com@-1468813552DEBUG  [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 PathDEBUG  [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 MethodDEBUG  [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 BrianConfigDEBUG  [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/(?[a-zA-Z-]*)/(?[a-zA-Z-]*)/v(?[0-9])/(?.*), replacement=/v${version}/${path}} to RewritePathDEBUG  [scheduling-1] 2021-09-12 21:33:55.043 o.s.c.g.r.RouteDefinitionRouteLocator - RouteDefinition matched: http://card-hk.free.beeceptor.com@-792698083DEBUG  [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 PathDEBUG  [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 RewritePathDEBUG  [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 BrianConfigDEBUG  [scheduling-1] 2021-09-12 21:33:55.046 o.s.c.g.r.RouteDefinitionRouteLocator - RouteDefinition matched: https://pancake.free.beeceptor.com@1912448187DEBUG  [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 PathDEBUG  [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 MethodDEBUG  [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 BrianConfigDEBUG  [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/(?[a-zA-Z-]*)/(?[a-zA-Z-]*)/v(?[0-9])/(?.*), replacement=/v${version}/${path}} to RewritePathDEBUG  [scheduling-1] 2021-09-12 21:33:55.049 o.s.c.g.r.RouteDefinitionRouteLocator - RouteDefinition matched: http://card-hk.free.beeceptor.com@-679151078

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

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

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

记得点「」和「在看」↓

爱你们

浏览 125
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报