目前SpringCloud Gateway只支持配置文件yml、properties和配置文件@Configuration的方式配置路由。这种方式有弊端,就是如果需要变更路由信息,修改路由规则,然后重启Gateway,修改或增加的路由规则才会生效。如果出现该问题,在Gateway重启的这段时间里,所有接入Gateway服务的应用都不可用。这肯定是不可行的。
Gateway默认的配置
yml配置文件
@Configuration
路由初始化
不管是什么方式配置路由,这些配置最后都会被封装到RouteDefinition
中
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| public class RouteDefinition { private String id; @NotEmpty @Valid private List<PredicateDefinition> predicates = new ArrayList<>(); @Valid private List<FilterDefinition> filters = new ArrayList<>(); @NotNull private URI uri; private Map<String, Object> metadata = new HashMap<>(); private int order = 0; public RouteDefinition() { } ... }
|
一个路由配置就是一个RouteDefinition
对象,一个RouteDefinition
对应一个ID,如果不设置,默认为uuid,所有路由信息在系统启动的时候被加载进内存里
如图框住的部分就是加载yml文件,它返回PropertiesRouteDefinitionLocator
对象,该对象实现RouteDefinitionLocator
接口,该接口是路由的装载器
1 2 3 4 5
| public interface RouteDefinitionLocator {
Flux<RouteDefinition> getRouteDefinitions();
}
|
该接口只有一个方法,就是获取路由配置,该接口有多个实现类,分别对应不同的配置路由的方式
- CachingRouteDefinitionLocator -RouteDefinitionLocator包装类, 缓存目标RouteDefinitionLocator 为routeDefinitions提供缓存功能
- CompositeRouteDefinitionLocator -RouteDefinitionLocator包装类,组合多种 RouteDefinitionLocator 的实现,为 routeDefinitions提供统一入口
- PropertiesRouteDefinitionLocator-从配置文件(GatewayProperties 例如,YML / Properties 等 ) 读取RouteDefinition
- DiscoveryClientRouteDefinitionLocator-从注册中心( 例如,Eureka / Consul / Zookeeper / Etcd 等 )读取RouteDefinition
- RouteDefinitionRepository-从存储器( 例如,内存 / Redis / MySQL 等 )读取RouteDefinition
初始化顺序是
- 配置文件加载初始化 PropertiesRouteDefinitionLocator–>CompositeRouteDefinitionLocator
- 存储器中加载初始化RouteDefinitionRepository–>CompositeRouteDefinitionLocator
- 注册中心加载初始化DiscoveryClientRouteDefinitionLocator–>CompositeRouteDefinitionLocator
而使用存储器中初始化的条件是,没有定义RouteDefinitionRepository
1 2 3 4 5
| @Bean @ConditionalOnMissingBean(RouteDefinitionRepository.class) public InMemoryRouteDefinitionRepository inMemoryRouteDefinitionRepository() { return new InMemoryRouteDefinitionRepository(); }
|
路由配置从数据库中获取
因此,我们可以通过实现RouteDefinitionRepository
接口来自定义路由配置的获取方式
实现RouteDefinitionRepository接口
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59
| @Component public class DBARouteDefinitionRepository implements RouteDefinitionRepository {
@Resource private GatewayDao gatewayDao;
@Override public Flux<RouteDefinition> getRouteDefinitions() { List<RouteDefinition> gatewayRouteEntityList = getRouteConfig(); return Flux.fromIterable(gatewayRouteEntityList); }
private List<RouteDefinition> getRouteConfig() { List<RoutesEntity> routesEntities = gatewayDao.queryAllRoutes();
List<RouteDefinition> definitions = new ArrayList<>();
for (RoutesEntity entity : routesEntities) { RouteDefinition definition = new RouteDefinition();
Map<String,String> predicateParams = new HashMap<>(); PredicateDefinition predicate = new PredicateDefinition();
Map<String,String> filterParams = new HashMap<>(); FilterDefinition filter = new FilterDefinition();
URI uri = UriComponentsBuilder.fromHttpUrl(entity.getUri()).build().toUri();
predicate.setName("Path"); predicateParams.put("pattern",entity.getPredicates()); predicate.setArgs(predicateParams);
definition.setPredicates(Arrays.asList(predicate));
definition.setUri(uri); definition.setId(entity.getRouteId());
definitions.add(definition); }
return definitions; }
@Override public Mono<Void> save(Mono<RouteDefinition> route) { return null; }
@Override public Mono<Void> delete(Mono<String> routeId) { return null; } }
|
动态路由
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
| @Service public class DBARouteConfigService implements ApplicationEventPublisherAware {
@Resource private GatewayDao gatewayDao;
private ApplicationEventPublisher applicationEventPublisher;
public int add(RoutesEntity entity){ gatewayDao.create(entity); applicationEventPublisher.publishEvent(new RefreshRoutesEvent(this)); return 1; }
public int delete(String routeId) { gatewayDao.deleteByRouteId(routeId); applicationEventPublisher.publishEvent(new RefreshRoutesEvent(this)); return 1; }
public int update(RoutesEntity entity) { gatewayDao.updateRouteById(entity); applicationEventPublisher.publishEvent(new RefreshRoutesEvent(this)); return 1; }
@Override public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) { this.applicationEventPublisher = applicationEventPublisher; } }
|
在数据库中修改删除路由配置,然后再执行一下RefreshRoutesEvent
事件,即可刷新路由配置,无需重启,立刻生效