Skip to content

Commit a5e16a6

Browse files
committed
Updates RetryFilterFunctions to support HttpMethods
1 parent bf3f60d commit a5e16a6

File tree

2 files changed

+59
-10
lines changed

2 files changed

+59
-10
lines changed

spring-cloud-gateway-server-mvc/src/main/java/org/springframework/cloud/gateway/server/mvc/filter/RetryFilterFunctions.java

Lines changed: 58 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@
2626
import java.util.concurrent.TimeoutException;
2727
import java.util.function.Consumer;
2828

29+
import org.springframework.core.NestedRuntimeException;
30+
import org.springframework.http.HttpMethod;
2931
import org.springframework.http.HttpStatus;
3032
import org.springframework.http.HttpStatusCode;
3133
import org.springframework.retry.RetryContext;
@@ -35,8 +37,8 @@
3537
import org.springframework.retry.policy.SimpleRetryPolicy;
3638
import org.springframework.retry.support.RetryTemplate;
3739
import org.springframework.retry.support.RetryTemplateBuilder;
38-
import org.springframework.web.client.HttpServerErrorException;
3940
import org.springframework.web.servlet.function.HandlerFilterFunction;
41+
import org.springframework.web.servlet.function.ServerRequest;
4042
import org.springframework.web.servlet.function.ServerResponse;
4143

4244
public abstract class RetryFilterFunctions {
@@ -56,14 +58,16 @@ public static HandlerFilterFunction<ServerResponse, ServerResponse> retry(Consum
5658
Map<Class<? extends Throwable>, Boolean> retryableExceptions = new HashMap<>();
5759
config.getExceptions().forEach(exception -> retryableExceptions.put(exception, true));
5860
SimpleRetryPolicy simpleRetryPolicy = new SimpleRetryPolicy(config.getRetries(), retryableExceptions);
59-
compositeRetryPolicy.setPolicies(
60-
Arrays.asList(simpleRetryPolicy, new HttpStatusRetryPolicy(config)).toArray(new RetryPolicy[0]));
61+
compositeRetryPolicy
62+
.setPolicies(Arrays.asList(simpleRetryPolicy, new HttpRetryPolicy(config)).toArray(new RetryPolicy[0]));
6163
RetryTemplate retryTemplate = retryTemplateBuilder.customPolicy(compositeRetryPolicy).build();
6264
return (request, next) -> retryTemplate.execute(context -> {
6365
ServerResponse serverResponse = next.handle(request);
6466

65-
if (isRetryableStatusCode(serverResponse.statusCode(), config)) {
66-
throw new HttpServerErrorException(serverResponse.statusCode());
67+
if (isRetryableStatusCode(serverResponse.statusCode(), config)
68+
&& isRetryableMethod(request.method(), config)) {
69+
// use this to transfer information to HttpStatusRetryPolicy
70+
throw new RetryException(request, serverResponse);
6771
}
6872
return serverResponse;
6973
});
@@ -73,19 +77,24 @@ private static boolean isRetryableStatusCode(HttpStatusCode httpStatus, RetryCon
7377
return config.getSeries().stream().anyMatch(series -> HttpStatus.Series.resolve(httpStatus.value()) == series);
7478
}
7579

76-
public static class HttpStatusRetryPolicy extends NeverRetryPolicy {
80+
private static boolean isRetryableMethod(HttpMethod method, RetryConfig config) {
81+
return config.methods.contains(method);
82+
}
83+
84+
public static class HttpRetryPolicy extends NeverRetryPolicy {
7785

7886
private final RetryConfig config;
7987

80-
public HttpStatusRetryPolicy(RetryConfig config) {
88+
public HttpRetryPolicy(RetryConfig config) {
8189
this.config = config;
8290
}
8391

8492
@Override
8593
public boolean canRetry(RetryContext context) {
8694
// TODO: custom exception
87-
if (context.getLastThrowable() instanceof HttpServerErrorException e) {
88-
return isRetryableStatusCode(e.getStatusCode(), config);
95+
if (context.getLastThrowable() instanceof RetryException e) {
96+
return isRetryableStatusCode(e.getResponse().statusCode(), config)
97+
&& isRetryableMethod(e.getRequest().method(), config);
8998
}
9099
return super.canRetry(context);
91100
}
@@ -99,9 +108,12 @@ public static class RetryConfig {
99108
private Set<HttpStatus.Series> series = new HashSet<>(List.of(HttpStatus.Series.SERVER_ERROR));
100109

101110
private Set<Class<? extends Throwable>> exceptions = new HashSet<>(
102-
List.of(IOException.class, TimeoutException.class, HttpServerErrorException.class));
111+
List.of(IOException.class, TimeoutException.class, RetryException.class));
112+
113+
private Set<HttpMethod> methods = new HashSet<>(List.of(HttpMethod.GET));
103114

104115
// TODO: individual statuses
116+
// TODO: backoff
105117
// TODO: support more Spring Retry policies
106118

107119
public int getRetries() {
@@ -141,6 +153,42 @@ public RetryConfig addExceptions(Class<? extends Throwable>... exceptions) {
141153
return this;
142154
}
143155

156+
public Set<HttpMethod> getMethods() {
157+
return methods;
158+
}
159+
160+
public RetryConfig setMethods(Set<HttpMethod> methods) {
161+
this.methods = methods;
162+
return this;
163+
}
164+
165+
public RetryConfig addMethods(HttpMethod... methods) {
166+
this.methods.addAll(Arrays.asList(methods));
167+
return this;
168+
}
169+
170+
}
171+
172+
private static class RetryException extends NestedRuntimeException {
173+
174+
private final ServerRequest request;
175+
176+
private final ServerResponse response;
177+
178+
RetryException(ServerRequest request, ServerResponse response) {
179+
super(null);
180+
this.request = request;
181+
this.response = response;
182+
}
183+
184+
public ServerRequest getRequest() {
185+
return request;
186+
}
187+
188+
public ServerResponse getResponse() {
189+
return response;
190+
}
191+
144192
}
145193

146194
}

spring-cloud-gateway-server-mvc/src/test/java/org/springframework/cloud/gateway/server/mvc/ServerMvcIntegrationTests.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -802,6 +802,7 @@ public RouterFunction<ServerResponse> gatewayRouterFunctionsRetry() {
802802
.route(path("/retry"), http())
803803
.before(new LocalServerPortUriResolver())
804804
.filter(retry(3))
805+
//.filter(retry(config -> config.setRetries(3).setSeries(Set.of(HttpStatus.Series.SERVER_ERROR)).setMethods(Set.of(HttpMethod.GET, HttpMethod.POST))))
805806
.filter(prefixPath("/do"))
806807
.build();
807808
// @formatter:on

0 commit comments

Comments
 (0)