在webflux环境下,spring-gateway从数据库读取路由配置,并且随着数据库修改,可以动态变化。
如果只实现从数据库中读取路由数据,只需要实现RouteDefinitionLocator接口或RouteDefinitionRepository接口即可。
RouteDefinitionRepository继承自RouteDefinitionLocator,用哪个没啥大区别
MyRouteDefinitionRepository.class
@Component
public class MyRouteDefinitionRepository implements RouteDefinitionLocator {
public Flux<RouteDefinition> getRouteDefinitions() {
// 从数据库中获取路由配置,并封装为RouteDefinition对象
}
}
但是如果想要在数据库数据发生变化时,重新加载路由配置,就需要额外的操作。
在GatewayAutoConfiguration类中,首先把所有的RouteDefinitionLocator实现类封装为一个RouteDefinitionLocator对象,并设为@Primary。
然后再把刚才已经封装的RouteDefinitionLocator对象以及配置文件或其他途径获取的路由配置,加一起再次封装为RouteLocator对象。
然后再把所有的RouteLocator实现类一起封装成一个RouteLocator对象,并设为@Primary,这个RouteLocator对象具体的实现类是CachingRouteLocator。
最后通过CachingRouteLocator类的getRouteDefinitions()方法获取最终的路由配置对象
GatewayAutoConfiguration.class
@Bean
@ConditionalOnMissingBean({RouteDefinitionRepository.class})
public InMemoryRouteDefinitionRepository inMemoryRouteDefinitionRepository() {
return new InMemoryRouteDefinitionRepository();
}
@Bean
@Primary
public RouteDefinitionLocator routeDefinitionLocator(List<RouteDefinitionLocator> routeDefinitionLocators) {
return new CompositeRouteDefinitionLocator(Flux.fromIterable(routeDefinitionLocators));
}
@Bean
public RouteLocator routeDefinitionRouteLocator(GatewayProperties properties, List<GatewayFilterFactory> GatewayFilters, List<RoutePredicateFactory> predicates, RouteDefinitionLocator routeDefinitionLocator, @Qualifier("webFluxConversionService") ConversionService conversionService) {
return new RouteDefinitionRouteLocator(routeDefinitionLocator, predicates, GatewayFilters, properties, conversionService);
}
@Bean
@Primary
public RouteLocator cachedCompositeRouteLocator(List<RouteLocator> routeLocators) {
return new CachingRouteLocator(new CompositeRouteLocator(Flux.fromIterable(routeLocators)));
}
在CachingRouteDefinitionLocator中可以看到,最终返回的是成员变量this.routeDefinitions,但是this.routeDefinitions在构造方法初始化时被赋予了一个响应式的结果
所以this.routeDefinitions的具体值是一个函数,该函数不会在构造方法内执行。
这个缓存函数的参数为上面封装的一个@Primary的RouteLocator对象,当前this.cache为空时,调用RouteLocator的getRouteDefinitions方法,这时才调用了我们实现的数据库方法。
于是该类提供了一个refresh方法,用来清空缓存。
该类监听了RefreshRoutesEvent事件,当产生事件时,即调用refresh方法。
CachingRouteDefinitionLocator.class
package org.springframework.cloud.gateway.route;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.springframework.cloud.gateway.event.RefreshRoutesEvent;
import org.springframework.context.ApplicationListener;
import reactor.cache.CacheFlux;
import reactor.cache.CacheFlux.FluxCacheBuilderMapMiss;
import reactor.core.publisher.Flux;
public class CachingRouteDefinitionLocator implements RouteDefinitionLocator, ApplicationListener<RefreshRoutesEvent> {
private final RouteDefinitionLocator delegate;
private final Flux<RouteDefinition> routeDefinitions;
private final Map<String, List> cache = new HashMap();
public CachingRouteDefinitionLocator(RouteDefinitionLocator delegate) {
this.delegate = delegate;
FluxCacheBuilderMapMiss var10001 = CacheFlux.lookup(this.cache, "routeDefs", RouteDefinition.class);
RouteDefinitionLocator var10002 = this.delegate;
var10002.getClass();
this.routeDefinitions = var10001.onCacheMissResume(var10002::getRouteDefinitions);
}
public Flux<RouteDefinition> getRouteDefinitions() {
return this.routeDefinitions;
}
public Flux<RouteDefinition> refresh() {
this.cache.clear();
return this.routeDefinitions;
}
public void onApplicationEvent(RefreshRoutesEvent event) {
this.refresh();
}
/** @deprecated */
@Deprecated
void handleRefresh() {
this.refresh();
}
}
于是我们可以创建一个事件发布器,来发布RefreshRoutesEvent事件,用来通知清除缓存。
RouteRefreshEventPublisher.class
@Component
public class RouteRefreshEventPublisher {
@Autowired
private ApplicationEventPublisher publisher;
public void publishRefreshEvent() {
publisher.publishEvent(new RefreshRoutesEvent(this));
}
}
最后,我们在数据库对应表增删改的时候,发布事件,等再次发生http请求时,即可重新加载路由配置。