上一篇博客(东小西:Gateway实现动态路由),简单实现了Gateway动态路由。我们实现的逻辑思路和 SpringBoot Admin 管理路由的思路大体一致,通过调用API管理路由,并未做路由配置持久化。
在使用SpringBoot Admin的时候,网关服务的菜单栏多一个Gateway的菜单。
Spring Boot Admin很好的支持了Gateway,可以直接在管理界面中查看相关的路由配置,添加或者删除。
SpringBoot Admin 能实现这些功能,是因为Gateway提供了相应的Actuator Endpoint接口来管理路由配置。
至于为什么我们在生产环境中,不通过这种方式实现动态路由呢?
Gateway提供的Actuator接口
在上篇博客中,使用过查看所有路由的接口:
GET http://ip:port/actuator/gateway/routes
问题分析
(1)在使用SpringBoot Admin 管理路由中删除路由,为么会出现删除失败的情况?
(2)添加的路由配置存放在哪里呢,有没有做持久化?
Spring Gateway Actuator源码分析
在 GatewayControllerEndpoint 类中,定义了api接口,比如删除、添加等接口。
@PostMapping("/routes/{id}")
@SuppressWarnings("unchecked")
public Mono<ResponseEntity<Object>> save(@PathVariable String id,
@RequestBody RouteDefinition route) {
return Mono.just(route).filter(this::validateRouteDefinition)
.flatMap(routeDefinition -> this.routeDefinitionWriter
.save(Mono.just(routeDefinition).map(r -> {
r.setId(id);
log.debug("Saving route: " + route);
return r;
}))
.then(Mono.defer(() -> Mono.just(ResponseEntity
.created(URI.create("/routes/" + id)).build()))))
.switchIfEmpty(
Mono.defer(() -> Mono.just(ResponseEntity.badRequest().build())));
}
private boolean validateRouteDefinition(RouteDefinition routeDefinition) {
boolean hasValidFilterDefinitions = routeDefinition.getFilters().stream()
.allMatch(filterDefinition -> GatewayFilters.stream()
.anyMatch(gatewayFilterFactory -> filterDefinition.getName()
.equals(gatewayFilterFactory.name())));
boolean hasValidPredicateDefinitions = routeDefinition.getPredicates().stream()
.allMatch(predicateDefinition -> routePredicates.stream()
.anyMatch(routePredicate -> predicateDefinition.getName()
.equals(routePredicate.name())));
log.debug("FilterDefinitions valid: " + hasValidFilterDefinitions);
log.debug("PredicateDefinitions valid: " + hasValidPredicateDefinitions);
return hasValidFilterDefinitions && hasValidPredicateDefinitions;
}
@DeleteMapping("/routes/{id}")
public Mono<ResponseEntity<Object>> delete(@PathVariable String id) {
return this.routeDefinitionWriter.delete(Mono.just(id))
.then(Mono.defer(() -> Mono.just(ResponseEntity.ok().build())))
.onErrorResume(t -> t instanceof NotFoundException,
t -> Mono.just(ResponseEntity.notFound().build()));
}
顺着 this.routeDefinitionWriter.save 在 RouteDefinitionWriter 的实现类 InMemoryRouteDefinitionRepository 中创建了一个以路由Id为key的路由存储 Map。
private final Map<String, RouteDefinition> routes = synchronizedMap(new LinkedHashMap<String, RouteDefinition>());
我们通过actuator接口在新增或者删除路由配置的时候,都是对routeDefinitionWriter对象中的routes这个Map进行操作。
为什么我们能看到在配置文件中配置的路由,但是又删除不了呢?
在源码中,你发现 /actuator/gateway/routes 这个接口获取的是 routeDefinitionLocator 中的路由配置,routeDefinitionLocator的类型是CompositeRouteDefinitionLocator,并且他的逻辑是把其他的所有RouteDefinitionLocator类型的都包含进去了,所以获取的是整个系统的全部路由配置。
所以,上面的问题便有了答案:
1、增加的路由配置是保存在内存中的,我们没有办法保存它。
2、删除只能删除通过接口增加的路由配置,配置文件中定义的不能删除。
在使用gateway动态的路由的时候,可使用redis缓存、或者是mysql等数据库做持久化。