WeightCalculatorWebFilter now forces subscription of RouteLocator.

Because it needs to calculate the weights before predicates run, if the
predicates are all defined in yaml, weights aren't initialized until
after the first run, meaning weights are unavailable in the predicates.

WeightCalculatorWebFilter now listens for RoutesRefreshedEvent. When
triggered, it subscribes to RouteLocator, forcing initialization.

fixes gh-332
This commit is contained in:
Spencer Gibb
2019-01-28 15:05:19 -05:00
parent 08408e37a2
commit e960eadb32
5 changed files with 124 additions and 8 deletions

View File

@@ -392,8 +392,8 @@ public class GatewayAutoConfiguration {
}
@Bean
public WeightCalculatorWebFilter weightCalculatorWebFilter(Validator validator) {
return new WeightCalculatorWebFilter(validator);
public WeightCalculatorWebFilter weightCalculatorWebFilter(Validator validator, ObjectProvider<RouteLocator> routeLocator) {
return new WeightCalculatorWebFilter(validator, routeLocator);
}
/*@Bean

View File

@@ -27,9 +27,13 @@ import java.util.concurrent.atomic.AtomicInteger;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.jetbrains.annotations.NotNull;
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.WeightDefinedEvent;
import org.springframework.cloud.gateway.route.RouteLocator;
import org.springframework.cloud.gateway.support.ConfigurationUtils;
import org.springframework.cloud.gateway.support.WeightConfig;
import org.springframework.context.ApplicationEvent;
@@ -43,8 +47,6 @@ import org.springframework.web.server.WebFilterChain;
import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.WEIGHT_ATTR;
import reactor.core.publisher.Mono;
/**
* @author Spencer Gibb
*/
@@ -55,17 +57,24 @@ public class WeightCalculatorWebFilter implements WebFilter, Ordered, SmartAppli
public static final int WEIGHT_CALC_FILTER_ORDER = 10001;
private final Validator validator;
private final ObjectProvider<RouteLocator> routeLocator;
private Random random = new Random();
private int order = WEIGHT_CALC_FILTER_ORDER;
private Map<String, GroupWeightConfig> groupWeights = new ConcurrentHashMap<>();
/* for testing */ WeightCalculatorWebFilter() {
this(null);
this(null, null);
}
@Deprecated
public WeightCalculatorWebFilter(Validator validator) {
this(validator, null);
}
public WeightCalculatorWebFilter(Validator validator, ObjectProvider<RouteLocator> routeLocator) {
this.validator = validator;
this.routeLocator = routeLocator;
}
@Override
@@ -84,7 +93,8 @@ public class WeightCalculatorWebFilter implements WebFilter, Ordered, SmartAppli
@Override
public boolean supportsEventType(Class<? extends ApplicationEvent> eventType) {
return PredicateArgsEvent.class.isAssignableFrom(eventType) || // config file
WeightDefinedEvent.class.isAssignableFrom(eventType); // java dsl
WeightDefinedEvent.class.isAssignableFrom(eventType) || // java dsl
RefreshRoutesEvent.class.isAssignableFrom(eventType); // force initialization
}
@Override
@@ -98,6 +108,8 @@ public class WeightCalculatorWebFilter implements WebFilter, Ordered, SmartAppli
handle((PredicateArgsEvent) event);
} else if (event instanceof WeightDefinedEvent) {
addWeightConfig(((WeightDefinedEvent)event).getWeightConfig());
} else if (event instanceof RefreshRoutesEvent && routeLocator != null) {
routeLocator.ifAvailable(locator -> locator.getRoutes().subscribe()); // forces initialization
}
}
@@ -197,7 +209,6 @@ public class WeightCalculatorWebFilter implements WebFilter, Ordered, SmartAppli
return chain.filter(exchange);
}
@NotNull
/* for testing */ static Map<String, String> getWeights(ServerWebExchange exchange) {
Map<String, String> weights = exchange.getAttribute(WEIGHT_ATTR);

View File

@@ -92,6 +92,8 @@ public class WeightRoutePredicateFactory extends AbstractRoutePredicateFactory<W
}
return routeId.equals(chosenRoute);
} else if (log.isTraceEnabled()) {
log.trace("no weights found for group: "+ group + ", current route: " + routeId);
}
return false;

View File

@@ -0,0 +1,81 @@
/*
* Copyright 2013-2019 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
*
* http://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.handler.predicate;
import java.util.Random;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringBootConfiguration;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.cloud.gateway.filter.WeightCalculatorWebFilter;
import org.springframework.cloud.gateway.test.BaseWebClientTests;
import org.springframework.context.annotation.Import;
import org.springframework.http.HttpHeaders;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.context.junit4.SpringRunner;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT;
@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = RANDOM_PORT)
@ActiveProfiles("weights-404")
@DirtiesContext
public class WeightRoutePredicateFactoryYaml404Tests extends BaseWebClientTests {
@Autowired
private WeightCalculatorWebFilter filter;
@Test
public void weightsFromYamlNot404() {
filter.setRandom(getRandom(0.5));
testClient.get().uri("/get")
.header(HttpHeaders.HOST, "www.weight4041.org")
.exchange()
.expectStatus().isOk()
.expectHeader().valueEquals(ROUTE_ID_HEADER, "weight_first_404_test_1");
}
@EnableAutoConfiguration
@SpringBootConfiguration
@Import(DefaultTestConfig.class)
public static class TestConfig {
public TestConfig(WeightCalculatorWebFilter filter) {
Random random = getRandom(0.4);
filter.setRandom(random);
}
}
private static Random getRandom(double value) {
Random random = mock(Random.class);
when(random.nextDouble())
.thenReturn(value);
return random;
}
}

View File

@@ -0,0 +1,22 @@
test:
uri: lb://testservice
spring:
cloud:
gateway:
default-filters:
- PrefixPath=/httpbin
routes:
# =====================================
- id: weight_first_404_test_1
uri: ${test.uri}
predicates:
- Host=**.weight4041.org
- Weight=group404, 8
# =====================================
- id: weight_first_404_test_2
uri: ${test.uri}
predicates:
- Host=**.weight4042.org
- Weight=group404, 2