SpringBoot Admin & Gateway 动态路由

399 阅读2分钟

上一篇博客(东小西: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.saveRouteDefinitionWriter 的实现类 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等数据库做持久化。