Skip to content

Commit ef16442

Browse files
authored
Merge pull request spring-projects#216 from ryanjbaxter/retry-on-status-codes
Retry requests on specified status codes. Fixes spring-projects#215.
2 parents 0c04159 + 87c319a commit ef16442

File tree

4 files changed

+57
-5
lines changed

4 files changed

+57
-5
lines changed

spring-cloud-commons/src/main/java/org/springframework/cloud/client/loadbalancer/LoadBalancerAutoConfiguration.java

+8-3
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ public void customize(RestTemplate restTemplate) {
103103

104104
@Configuration
105105
@ConditionalOnClass(RetryTemplate.class)
106-
static class RetryAutoConfiguration {
106+
public static class RetryAutoConfiguration {
107107
@Bean
108108
public RetryTemplate retryTemplate() {
109109
RetryTemplate template = new RetryTemplate();
@@ -116,13 +116,18 @@ public RetryTemplate retryTemplate() {
116116
public LoadBalancedRetryPolicyFactory loadBalancedRetryPolicyFactory() {
117117
return new LoadBalancedRetryPolicyFactory.NeverRetryFactory();
118118
}
119+
}
119120

121+
@Configuration
122+
@ConditionalOnClass(RetryTemplate.class)
123+
public static class RetryInterceptorAutoConfiguration {
120124
@Bean
125+
@ConditionalOnMissingBean
121126
public RetryLoadBalancerInterceptor ribbonInterceptor(
122127
LoadBalancerClient loadBalancerClient, LoadBalancerRetryProperties properties,
123128
LoadBalancedRetryPolicyFactory lbRetryPolicyFactory,
124-
LoadBalancerRequestFactory requestFactory) {
125-
return new RetryLoadBalancerInterceptor(loadBalancerClient, retryTemplate(), properties,
129+
LoadBalancerRequestFactory requestFactory, RetryTemplate retryTemplate) {
130+
return new RetryLoadBalancerInterceptor(loadBalancerClient, retryTemplate, properties,
126131
lbRetryPolicyFactory, requestFactory);
127132
}
128133

spring-cloud-commons/src/main/java/org/springframework/cloud/client/loadbalancer/RetryLoadBalancerInterceptor.java

+6-2
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ public ClientHttpResponse intercept(final HttpRequest request, final byte[] body
6868
final URI originalUri = request.getURI();
6969
final String serviceName = originalUri.getHost();
7070
Assert.state(serviceName != null, "Request URI does not contain a valid hostname: " + originalUri);
71-
LoadBalancedRetryPolicy retryPolicy = lbRetryPolicyFactory.create(serviceName,
71+
final LoadBalancedRetryPolicy retryPolicy = lbRetryPolicyFactory.create(serviceName,
7272
loadBalancer);
7373
retryTemplate.setRetryPolicy(
7474
!lbProperties.isEnabled() || retryPolicy == null ? new NeverRetryPolicy()
@@ -87,9 +87,13 @@ public ClientHttpResponse doWithRetry(RetryContext context)
8787
if (serviceInstance == null) {
8888
serviceInstance = loadBalancer.choose(serviceName);
8989
}
90-
return RetryLoadBalancerInterceptor.this.loadBalancer.execute(
90+
ClientHttpResponse response = RetryLoadBalancerInterceptor.this.loadBalancer.execute(
9191
serviceName, serviceInstance,
9292
requestFactory.createRequest(request, body, execution));
93+
if(retryPolicy != null && retryPolicy.retryableStatusCode(response.getRawStatusCode())) {
94+
throw new RetryableStatusCodeException(serviceName, response.getRawStatusCode());
95+
}
96+
return response;
9397
}
9498
});
9599
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
package org.springframework.cloud.client.loadbalancer;
2+
3+
import java.io.IOException;
4+
5+
/**
6+
* Exception to be thrown when the status code is deemed to be retryable.
7+
* @author Ryan Baxter
8+
*/
9+
public class RetryableStatusCodeException extends IOException {
10+
11+
private static final String MESSAGE = "Service %s returned a status code of %d";
12+
13+
public RetryableStatusCodeException(String serviceId, int statusCode) {
14+
super(String.format(MESSAGE, serviceId, statusCode));
15+
}
16+
}

spring-cloud-commons/src/test/java/org/springframework/cloud/client/loadbalancer/RetryLoadBalancerInterceptorTest.java

+27
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,33 @@ public void interceptSuccess() throws Throwable {
132132
verify(lbRequestFactory).createRequest(request, body, execution);
133133
}
134134

135+
@Test
136+
public void interceptRetryOnStatusCode() throws Throwable {
137+
HttpRequest request = mock(HttpRequest.class);
138+
when(request.getURI()).thenReturn(new URI("http://foo"));
139+
ClientHttpResponse clientHttpResponseNotFound = new MockClientHttpResponse(new byte[]{}, HttpStatus.NOT_FOUND);
140+
ClientHttpResponse clientHttpResponseOk = new MockClientHttpResponse(new byte[]{}, HttpStatus.OK);
141+
LoadBalancedRetryPolicy policy = mock(LoadBalancedRetryPolicy.class);
142+
when(policy.retryableStatusCode(eq(HttpStatus.NOT_FOUND.value()))).thenReturn(true);
143+
when(policy.canRetryNextServer(any(LoadBalancedRetryContext.class))).thenReturn(true);
144+
InterceptorRetryPolicy interceptorRetryPolicy = new InterceptorRetryPolicy(request, policy, client,"foo");
145+
LoadBalancedRetryPolicyFactory lbRetryPolicyFactory = mock(LoadBalancedRetryPolicyFactory.class);
146+
when(lbRetryPolicyFactory.create(eq("foo"), any(ServiceInstanceChooser.class))).thenReturn(policy);
147+
ServiceInstance serviceInstance = mock(ServiceInstance.class);
148+
when(client.choose(eq("foo"))).thenReturn(serviceInstance);
149+
when(client.execute(eq("foo"), eq(serviceInstance), any(LoadBalancerRequest.class))).
150+
thenReturn(clientHttpResponseNotFound).thenReturn(clientHttpResponseOk);
151+
lbProperties.setEnabled(true);
152+
RetryLoadBalancerInterceptor interceptor = new RetryLoadBalancerInterceptor(client, retryTemplate, lbProperties, lbRetryPolicyFactory, lbRequestFactory);
153+
byte[] body = new byte[]{};
154+
ClientHttpRequestExecution execution = mock(ClientHttpRequestExecution.class);
155+
ClientHttpResponse rsp = interceptor.intercept(request, body, execution);
156+
verify(client, times(2)).execute(eq("foo"), eq(serviceInstance), any(LoadBalancerRequest.class));
157+
assertThat(rsp, is(clientHttpResponseOk));
158+
verify(retryTemplate, times(1)).setRetryPolicy(eq(interceptorRetryPolicy));
159+
verify(lbRequestFactory, times(2)).createRequest(request, body, execution);
160+
}
161+
135162
@Test
136163
public void interceptRetry() throws Throwable {
137164
HttpRequest request = mock(HttpRequest.class);

0 commit comments

Comments
 (0)