diff --git a/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/actuate/AbstractGatewayControllerEndpoint.java b/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/actuate/AbstractGatewayControllerEndpoint.java index b6855992..c1f57892 100644 --- a/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/actuate/AbstractGatewayControllerEndpoint.java +++ b/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/actuate/AbstractGatewayControllerEndpoint.java @@ -34,6 +34,7 @@ import reactor.core.publisher.Mono; import org.springframework.boot.actuate.autoconfigure.endpoint.web.WebEndpointProperties; import org.springframework.cloud.gateway.event.RefreshRoutesEvent; +import org.springframework.cloud.gateway.event.RouteDeletedEvent; import org.springframework.cloud.gateway.filter.FilterDefinition; import org.springframework.cloud.gateway.filter.GlobalFilter; import org.springframework.cloud.gateway.filter.factory.GatewayFilterFactory; @@ -307,9 +308,10 @@ public class AbstractGatewayControllerEndpoint implements ApplicationEventPublis @DeleteMapping("/routes/{id}") public Mono> 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())); + return this.routeDefinitionWriter.delete(Mono.just(id)).then(Mono.defer(() -> { + publisher.publishEvent(new RouteDeletedEvent(this, id)); + return Mono.just(ResponseEntity.ok().build()); + })).onErrorResume(t -> t instanceof NotFoundException, t -> Mono.just(ResponseEntity.notFound().build())); } @GetMapping("/routes/{id}/combinedfilters") diff --git a/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/event/RouteDeletedEvent.java b/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/event/RouteDeletedEvent.java new file mode 100644 index 00000000..9df16f9f --- /dev/null +++ b/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/event/RouteDeletedEvent.java @@ -0,0 +1,34 @@ +/* + * Copyright 2013-2024 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.cloud.gateway.event; + +import org.springframework.context.ApplicationEvent; + +public class RouteDeletedEvent extends ApplicationEvent { + + private final String routeId; + + public RouteDeletedEvent(Object source, String routeId) { + super(source); + this.routeId = routeId; + } + + public String getRouteId() { + return routeId; + } + +} diff --git a/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/filter/WeightCalculatorWebFilter.java b/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/filter/WeightCalculatorWebFilter.java index a61e2e69..d3810a08 100644 --- a/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/filter/WeightCalculatorWebFilter.java +++ b/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/filter/WeightCalculatorWebFilter.java @@ -35,6 +35,7 @@ import reactor.core.publisher.Mono; import org.springframework.beans.factory.ObjectProvider; import org.springframework.cloud.gateway.event.PredicateArgsEvent; import org.springframework.cloud.gateway.event.RefreshRoutesEvent; +import org.springframework.cloud.gateway.event.RouteDeletedEvent; import org.springframework.cloud.gateway.event.WeightDefinedEvent; import org.springframework.cloud.gateway.route.RouteLocator; import org.springframework.cloud.gateway.support.ConfigurationService; @@ -42,6 +43,7 @@ import org.springframework.cloud.gateway.support.WeightConfig; import org.springframework.context.ApplicationEvent; import org.springframework.context.event.SmartApplicationListener; import org.springframework.core.Ordered; +import org.springframework.core.log.LogMessage; import org.springframework.core.style.ToStringCreator; import org.springframework.util.Assert; import org.springframework.web.server.ServerWebExchange; @@ -123,6 +125,8 @@ public class WeightCalculatorWebFilter implements WebFilter, Ordered, SmartAppli return PredicateArgsEvent.class.isAssignableFrom(eventType) || // from java dsl WeightDefinedEvent.class.isAssignableFrom(eventType) || + // from actuator or custom call + RouteDeletedEvent.class.isAssignableFrom(eventType) || // force initialization RefreshRoutesEvent.class.isAssignableFrom(eventType); } @@ -140,6 +144,9 @@ public class WeightCalculatorWebFilter implements WebFilter, Ordered, SmartAppli else if (event instanceof WeightDefinedEvent) { addWeightConfig(((WeightDefinedEvent) event).getWeightConfig()); } + else if (event instanceof RouteDeletedEvent) { + removeWeightConfig(((RouteDeletedEvent) event).getRouteId()); + } else if (event instanceof RefreshRoutesEvent && routeLocator != null) { // forces initialization if (routeLocatorInitialized.compareAndSet(false, true)) { @@ -229,6 +236,16 @@ public class WeightCalculatorWebFilter implements WebFilter, Ordered, SmartAppli groupWeights.put(group, config); } + private void removeWeightConfig(String routeId) { + log.trace(LogMessage.format("Removing weight config for route %s", routeId)); + groupWeights.forEach((group, weightConfig) -> { + if (weightConfig.normalizedWeights.containsKey(routeId)) { + weightConfig.normalizedWeights.remove(routeId); + weightConfig.weights.remove(routeId); + } + }); + } + /* for testing */ Map getGroupWeights() { return groupWeights; } diff --git a/spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/filter/WeightCalculatorWebFilterTests.java b/spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/filter/WeightCalculatorWebFilterTests.java index adbb0a22..7471b705 100644 --- a/spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/filter/WeightCalculatorWebFilterTests.java +++ b/spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/filter/WeightCalculatorWebFilterTests.java @@ -24,6 +24,7 @@ import java.util.function.Supplier; import org.junit.jupiter.api.Test; import org.springframework.cloud.gateway.event.PredicateArgsEvent; +import org.springframework.cloud.gateway.event.RouteDeletedEvent; import org.springframework.cloud.gateway.filter.WeightCalculatorWebFilter.GroupWeightConfig; import org.springframework.cloud.gateway.support.ConfigurationService; import org.springframework.cloud.gateway.support.WeightConfig; @@ -121,6 +122,12 @@ public class WeightCalculatorWebFilterTests { filter.filter(exchange, filterChain); weights = WeightCalculatorWebFilter.getWeights(exchange); assertThat(weights).containsEntry("groupa", "route3"); + + filter.onApplicationEvent(new RouteDeletedEvent(this, "route3")); + assertThat(filter.getGroupWeights()).containsKey("groupa"); + GroupWeightConfig groupa = filter.getGroupWeights().get("groupa"); + assertThat(groupa.normalizedWeights).doesNotContainKey("route3"); + assertThat(groupa.weights).doesNotContainKey("route3"); } @Test