Merge pull request #216 from ryanjbaxter/retry-on-status-codes
Retry requests on specified status codes. Fixes #215.
This commit is contained in:
@@ -103,7 +103,7 @@ public class LoadBalancerAutoConfiguration {
|
||||
|
||||
@Configuration
|
||||
@ConditionalOnClass(RetryTemplate.class)
|
||||
static class RetryAutoConfiguration {
|
||||
public static class RetryAutoConfiguration {
|
||||
@Bean
|
||||
public RetryTemplate retryTemplate() {
|
||||
RetryTemplate template = new RetryTemplate();
|
||||
@@ -116,13 +116,18 @@ public class LoadBalancerAutoConfiguration {
|
||||
public LoadBalancedRetryPolicyFactory loadBalancedRetryPolicyFactory() {
|
||||
return new LoadBalancedRetryPolicyFactory.NeverRetryFactory();
|
||||
}
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@ConditionalOnClass(RetryTemplate.class)
|
||||
public static class RetryInterceptorAutoConfiguration {
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public RetryLoadBalancerInterceptor ribbonInterceptor(
|
||||
LoadBalancerClient loadBalancerClient, LoadBalancerRetryProperties properties,
|
||||
LoadBalancedRetryPolicyFactory lbRetryPolicyFactory,
|
||||
LoadBalancerRequestFactory requestFactory) {
|
||||
return new RetryLoadBalancerInterceptor(loadBalancerClient, retryTemplate(), properties,
|
||||
LoadBalancerRequestFactory requestFactory, RetryTemplate retryTemplate) {
|
||||
return new RetryLoadBalancerInterceptor(loadBalancerClient, retryTemplate, properties,
|
||||
lbRetryPolicyFactory, requestFactory);
|
||||
}
|
||||
|
||||
|
||||
@@ -68,7 +68,7 @@ public class RetryLoadBalancerInterceptor implements ClientHttpRequestIntercepto
|
||||
final URI originalUri = request.getURI();
|
||||
final String serviceName = originalUri.getHost();
|
||||
Assert.state(serviceName != null, "Request URI does not contain a valid hostname: " + originalUri);
|
||||
LoadBalancedRetryPolicy retryPolicy = lbRetryPolicyFactory.create(serviceName,
|
||||
final LoadBalancedRetryPolicy retryPolicy = lbRetryPolicyFactory.create(serviceName,
|
||||
loadBalancer);
|
||||
retryTemplate.setRetryPolicy(
|
||||
!lbProperties.isEnabled() || retryPolicy == null ? new NeverRetryPolicy()
|
||||
@@ -87,9 +87,13 @@ public class RetryLoadBalancerInterceptor implements ClientHttpRequestIntercepto
|
||||
if (serviceInstance == null) {
|
||||
serviceInstance = loadBalancer.choose(serviceName);
|
||||
}
|
||||
return RetryLoadBalancerInterceptor.this.loadBalancer.execute(
|
||||
ClientHttpResponse response = RetryLoadBalancerInterceptor.this.loadBalancer.execute(
|
||||
serviceName, serviceInstance,
|
||||
requestFactory.createRequest(request, body, execution));
|
||||
if(retryPolicy != null && retryPolicy.retryableStatusCode(response.getRawStatusCode())) {
|
||||
throw new RetryableStatusCodeException(serviceName, response.getRawStatusCode());
|
||||
}
|
||||
return response;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
package org.springframework.cloud.client.loadbalancer;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* Exception to be thrown when the status code is deemed to be retryable.
|
||||
* @author Ryan Baxter
|
||||
*/
|
||||
public class RetryableStatusCodeException extends IOException {
|
||||
|
||||
private static final String MESSAGE = "Service %s returned a status code of %d";
|
||||
|
||||
public RetryableStatusCodeException(String serviceId, int statusCode) {
|
||||
super(String.format(MESSAGE, serviceId, statusCode));
|
||||
}
|
||||
}
|
||||
@@ -132,6 +132,33 @@ public class RetryLoadBalancerInterceptorTest {
|
||||
verify(lbRequestFactory).createRequest(request, body, execution);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void interceptRetryOnStatusCode() throws Throwable {
|
||||
HttpRequest request = mock(HttpRequest.class);
|
||||
when(request.getURI()).thenReturn(new URI("http://foo"));
|
||||
ClientHttpResponse clientHttpResponseNotFound = new MockClientHttpResponse(new byte[]{}, HttpStatus.NOT_FOUND);
|
||||
ClientHttpResponse clientHttpResponseOk = new MockClientHttpResponse(new byte[]{}, HttpStatus.OK);
|
||||
LoadBalancedRetryPolicy policy = mock(LoadBalancedRetryPolicy.class);
|
||||
when(policy.retryableStatusCode(eq(HttpStatus.NOT_FOUND.value()))).thenReturn(true);
|
||||
when(policy.canRetryNextServer(any(LoadBalancedRetryContext.class))).thenReturn(true);
|
||||
InterceptorRetryPolicy interceptorRetryPolicy = new InterceptorRetryPolicy(request, policy, client,"foo");
|
||||
LoadBalancedRetryPolicyFactory lbRetryPolicyFactory = mock(LoadBalancedRetryPolicyFactory.class);
|
||||
when(lbRetryPolicyFactory.create(eq("foo"), any(ServiceInstanceChooser.class))).thenReturn(policy);
|
||||
ServiceInstance serviceInstance = mock(ServiceInstance.class);
|
||||
when(client.choose(eq("foo"))).thenReturn(serviceInstance);
|
||||
when(client.execute(eq("foo"), eq(serviceInstance), any(LoadBalancerRequest.class))).
|
||||
thenReturn(clientHttpResponseNotFound).thenReturn(clientHttpResponseOk);
|
||||
lbProperties.setEnabled(true);
|
||||
RetryLoadBalancerInterceptor interceptor = new RetryLoadBalancerInterceptor(client, retryTemplate, lbProperties, lbRetryPolicyFactory, lbRequestFactory);
|
||||
byte[] body = new byte[]{};
|
||||
ClientHttpRequestExecution execution = mock(ClientHttpRequestExecution.class);
|
||||
ClientHttpResponse rsp = interceptor.intercept(request, body, execution);
|
||||
verify(client, times(2)).execute(eq("foo"), eq(serviceInstance), any(LoadBalancerRequest.class));
|
||||
assertThat(rsp, is(clientHttpResponseOk));
|
||||
verify(retryTemplate, times(1)).setRetryPolicy(eq(interceptorRetryPolicy));
|
||||
verify(lbRequestFactory, times(2)).createRequest(request, body, execution);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void interceptRetry() throws Throwable {
|
||||
HttpRequest request = mock(HttpRequest.class);
|
||||
|
||||
Reference in New Issue
Block a user