Binds configuration to Configurable factory beans directly using Boot Binder.

Deprecates the use of Tuple.

Depracates ArgumentHints in favor of ShortcutConfigurable.

Creates Configurable interface to allow factories to create a
configuration object that can be bound to properties.

Creates StatefulConfigurable interface that extends Configurable and
allows singlton beans (like RedisRateLimiter) to store a map of
configuration keyed on route id. These beans use events like
FilterArgsEvent and PredicateArgsEvent to gather configuration as it is
read for each route.
This commit is contained in:
Spencer Gibb
2018-03-05 16:36:48 -05:00
parent fb4a425f56
commit 589ed9c4f3
92 changed files with 2202 additions and 935 deletions

View File

@@ -41,7 +41,7 @@ import org.springframework.web.reactive.function.server.ServerResponse;
public class GatewaySampleApplication {
@Bean
public RouteLocator customRouteLocator(RouteLocatorBuilder builder, ThrottleGatewayFilterFactory throttle) {
public RouteLocator customRouteLocator(RouteLocatorBuilder builder) {
//@formatter:off
return builder.routes()
.route(r -> r.host("**.abc.org").and().path("/image/png")
@@ -56,21 +56,17 @@ public class GatewaySampleApplication {
)
.route(r -> r.order(-1)
.host("**.throttle.org").and().path("/get")
.filters(f -> f.filter(throttle.apply(1,
1,
10,
TimeUnit.SECONDS)))
.filters(f -> f.filter(new ThrottleGatewayFilter()
.setCapacity(1)
.setRefillTokens(1)
.setRefillPeriod(10)
.setRefillUnit(TimeUnit.SECONDS)))
.uri("http://httpbin.org:80")
)
.build();
//@formatter:on
}
@Bean
public ThrottleGatewayFilterFactory throttleWebFilterFactory() {
return new ThrottleGatewayFilterFactory();
}
@Bean
public RouterFunction<ServerResponse> testFunRouterFunction() {
RouterFunction<ServerResponse> route = RouterFunctions.route(

View File

@@ -0,0 +1,98 @@
/*
* Copyright 2013-2017 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.sample;
import java.util.concurrent.TimeUnit;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.isomorphism.util.TokenBucket;
import org.isomorphism.util.TokenBuckets;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.http.HttpStatus;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
/**
* Sample throttling filter.
* See https://github.com/bbeck/token-bucket
*/
public class ThrottleGatewayFilter implements GatewayFilter {
private static final Log log = LogFactory.getLog(ThrottleGatewayFilter.class);
int capacity;
int refillTokens;
int refillPeriod;
TimeUnit refillUnit;
public int getCapacity() {
return capacity;
}
public ThrottleGatewayFilter setCapacity(int capacity) {
this.capacity = capacity;
return this;
}
public int getRefillTokens() {
return refillTokens;
}
public ThrottleGatewayFilter setRefillTokens(int refillTokens) {
this.refillTokens = refillTokens;
return this;
}
public int getRefillPeriod() {
return refillPeriod;
}
public ThrottleGatewayFilter setRefillPeriod(int refillPeriod) {
this.refillPeriod = refillPeriod;
return this;
}
public TimeUnit getRefillUnit() {
return refillUnit;
}
public ThrottleGatewayFilter setRefillUnit(TimeUnit refillUnit) {
this.refillUnit = refillUnit;
return this;
}
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
TokenBucket tokenBucket = TokenBuckets.builder()
.withCapacity(capacity)
.withFixedIntervalRefillStrategy(refillTokens, refillPeriod, refillUnit)
.build();
//TODO: get a token bucket for a key
log.debug("TokenBucket capacity: " + tokenBucket.getCapacity());
boolean consumed = tokenBucket.tryConsume();
if (consumed) {
return chain.filter(exchange);
}
exchange.getResponse().setStatusCode(HttpStatus.TOO_MANY_REQUESTS);
return exchange.getResponse().setComplete();
}
}

View File

@@ -1,65 +0,0 @@
/*
* Copyright 2013-2017 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.sample;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.isomorphism.util.TokenBucket;
import org.isomorphism.util.TokenBuckets;
import org.springframework.cloud.gateway.filter.factory.GatewayFilterFactory;
import org.springframework.http.HttpStatus;
import org.springframework.tuple.Tuple;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import java.util.concurrent.TimeUnit;
/**
* Sample throttling filter.
* See https://github.com/bbeck/token-bucket
*/
public class ThrottleGatewayFilterFactory implements GatewayFilterFactory {
private Log log = LogFactory.getLog(getClass());
@Override
public GatewayFilter apply(Tuple args) {
int capacity = args.getInt("capacity");
int refillTokens = args.getInt("refillTokens");
int refillPeriod = args.getInt("refillPeriod");
TimeUnit refillUnit = TimeUnit.valueOf(args.getString("refillUnit"));
return apply(capacity, refillTokens, refillPeriod, refillUnit);
}
public GatewayFilter apply(int capacity, int refillTokens, int refillPeriod, TimeUnit refillUnit) {
final TokenBucket tokenBucket = TokenBuckets.builder()
.withCapacity(capacity)
.withFixedIntervalRefillStrategy(refillTokens, refillPeriod, refillUnit)
.build();
return (exchange, chain) -> {
//TODO: get a token bucket for a key
log.debug("TokenBucket capacity: " + tokenBucket.getCapacity());
boolean consumed = tokenBucket.tryConsume();
if (consumed) {
return chain.filter(exchange);
}
exchange.getResponse().setStatusCode(HttpStatus.TOO_MANY_REQUESTS);
return exchange.getResponse().setComplete();
};
}
}

View File

@@ -28,6 +28,8 @@ import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.reactive.server.WebTestClient;
import org.springframework.util.SocketUtils;
import java.time.Duration;
import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT;
/**
@@ -59,7 +61,7 @@ public class GatewaySampleApplicationTests {
@Before
public void setup() {
baseUri = "http://localhost:" + port;
this.webClient = WebTestClient.bindToServer().baseUrl(baseUri).build();
this.webClient = WebTestClient.bindToServer().responseTimeout(Duration.ofSeconds(10)).baseUrl(baseUri).build();
}
@Test