Remove routed attribute when executing fallbacks in circuit breakers. Fixes #1421

This commit is contained in:
Ryan Baxter
2019-12-17 19:50:10 -05:00
parent 259d4bd8ad
commit 0732b51d10
6 changed files with 51 additions and 1 deletions

View File

@@ -50,6 +50,7 @@ import org.springframework.web.util.UriComponentsBuilder;
import static java.util.Collections.singletonList;
import static java.util.Optional.ofNullable;
import static org.springframework.cloud.gateway.support.GatewayToStringStyler.filterToStringCreator;
import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.GATEWAY_ALREADY_ROUTED_ATTR;
import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR;
import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.HYSTRIX_EXECUTION_EXCEPTION_ATTR;
import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.containsEncodedParts;
@@ -272,6 +273,10 @@ public class HystrixGatewayFilterFactory
ServerHttpRequest request = this.exchange.getRequest().mutate()
.uri(requestUrl).build();
ServerWebExchange mutated = exchange.mutate().request(request).build();
// Before we continue on remove the already routed attribute since the
// fallback may go back through the route handler if the fallback
// is to another route in the Gateway
mutated.getAttributes().remove(GATEWAY_ALREADY_ROUTED_ATTR);
return RxReactiveStreams.toObservable(getDispatcherHandler().handle(mutated));
}

View File

@@ -37,6 +37,7 @@ import static java.util.Collections.singletonList;
import static java.util.Optional.ofNullable;
import static org.springframework.cloud.gateway.support.GatewayToStringStyler.filterToStringCreator;
import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.CIRCUITBREAKER_EXECUTION_EXCEPTION_ATTR;
import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.GATEWAY_ALREADY_ROUTED_ATTR;
import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR;
import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.containsEncodedParts;
@@ -99,6 +100,11 @@ public abstract class SpringCloudCircuitBreakerFilterFactory extends
exchange.getAttributes().put(GATEWAY_REQUEST_URL_ATTR, requestUrl);
addExceptionDetails(t, exchange);
// Before we continue on remove the already routed attribute since the
// fallback may go back through the route handler if the fallback
// is to another route in the Gateway
exchange.getAttributes().remove(GATEWAY_ALREADY_ROUTED_ATTR);
ServerHttpRequest request = exchange.getRequest().mutate()
.uri(requestUrl).build();
return getDispatcherHandler()

View File

@@ -139,4 +139,11 @@ public class HystrixGatewayFilterFactoryTests extends BaseWebClientTests {
assertThat(filter.toString()).contains("myname").contains("forward:/myfallback");
}
@Test
public void filterFallbackForward() {
testClient.get().uri("/delay/3?a=c").header("Host", "www.hystrixforward.org")
.exchange().expectStatus().isOk().expectBody()
.json("{\"from\":\"hystrixfallbackcontroller3\"}");
}
}

View File

@@ -66,12 +66,23 @@ public class HystrixTestConfig {
return Collections.singletonMap("from", "fallbackcontroller2");
}
@RequestMapping("/hystrixFallbackController3")
public Map<String, String> fallbackcontroller3() {
return Collections.singletonMap("from", "hystrixfallbackcontroller3");
}
@Bean
public RouteLocator hystrixRouteLocator(RouteLocatorBuilder builder) {
return builder.routes().route("hystrix_java", r -> r.host("**.hystrixjava.org")
.filters(f -> f.prefixPath("/httpbin").hystrix(
config -> config.setFallbackUri("forward:/fallbackcontroller2")))
.uri(uri))
.route("hystrix_fallback_forward", r -> r.host("**.hystrixforward.org")
.filters(f -> f.hystrix(
config -> config.setFallbackUri("forward:/fallback")))
.uri(uri))
.route("hystrix_fallback_controller_3", r -> r.path("/fallback")
.filters(f -> f.setPath("/hystrixFallbackController3")).uri(uri))
.route("hystrix_connection_failure",
r -> r.host("**.hystrixconnectfail.org")
.filters(f -> f.prefixPath("/httpbin").hystrix(config -> {

View File

@@ -96,4 +96,12 @@ public abstract class SpringCloudCircuitBreakerFilterFactoryTests
.isNotEmpty().jsonPath("$.error").isEqualTo("Internal Server Error");
}
@Test
public void filterFallbackForward() {
testClient.get().uri("/delay/3?a=c")
.header("Host", "www.circuitbreakerforward.org").exchange().expectStatus()
.isOk().expectBody()
.json("{\"from\":\"circuitbreakerfallbackcontroller3\"}");
}
}

View File

@@ -64,9 +64,22 @@ public class SpringCloudCircuitBreakerTestConfig {
return Collections.singletonMap("from", "circuitbreakerfallbackcontroller2");
}
@RequestMapping("/circuitbreakerFallbackController3")
public Map<String, String> fallbackcontroller3() {
return Collections.singletonMap("from", "circuitbreakerfallbackcontroller3");
}
@Bean
public RouteLocator circuitBreakerRouteLocator(RouteLocatorBuilder builder) {
return builder.routes()
return builder.routes().route("circuitbreaker_fallback_forward",
r -> r.host("**.circuitbreakerforward.org")
.filters(f -> f.circuitBreaker(
config -> config.setFallbackUri("forward:/fallback")))
.uri(uri))
.route("fallback_controller_3",
r -> r.path("/fallback").filters(
f -> f.setPath("/circuitbreakerFallbackController3"))
.uri(uri))
.route("circuitbreaker_java", r -> r.host("**.circuitbreakerjava.org")
.filters(f -> f.prefixPath("/httpbin")
.circuitBreaker(config -> config.setFallbackUri(