Merge branch 'master' into 2.0.x

This commit is contained in:
Spencer Gibb
2017-11-21 19:19:50 -05:00
7 changed files with 130 additions and 10 deletions

View File

@@ -405,6 +405,26 @@ The properties you can use are `client.ribbon.MaxAutoRetries`,
See the https://github.com/Netflix/ribbon/wiki/Getting-Started#the-properties-file-sample-clientproperties[Ribbon documentation]
for a description of what there properties do.
If you would like to implement a `BackOffPolicy` in your retries you will need to
create a bean of type `LoadBalancedBackOffPolicyFactory`, and return the `BackOffPolicy`
you would like to use for a given service.
[source,java,indent=0]
----
@Configuration
public class MyConfiguration {
@Bean
LoadBalancedBackOffPolicyFactory backOffPolciyFactory() {
return new LoadBalancedBackOffPolicyFactory() {
@Override
public BackOffPolicy createBackOffPolicy(String service) {
return new ExponentialBackOffPolicy();
}
};
}
}
----
NOTE: `client` in the above examples should be replaced with your Ribbon client's
name.

View File

@@ -0,0 +1,21 @@
package org.springframework.cloud.client.loadbalancer;
import org.springframework.retry.backoff.BackOffPolicy;
import org.springframework.retry.backoff.NoBackOffPolicy;
/**
* Factory class to return the backoff policy.
* @author Ryan Baxter
*/
public interface LoadBalancedBackOffPolicyFactory {
public BackOffPolicy createBackOffPolicy(String service);
static class NoBackOffPolicyFactory implements LoadBalancedBackOffPolicyFactory {
@Override
public BackOffPolicy createBackOffPolicy(String service) {
return new NoBackOffPolicy();
}
}
}

View File

@@ -116,6 +116,12 @@ public class LoadBalancerAutoConfiguration {
public LoadBalancedRetryPolicyFactory loadBalancedRetryPolicyFactory() {
return new LoadBalancedRetryPolicyFactory.NeverRetryFactory();
}
@Bean
@ConditionalOnMissingBean
public LoadBalancedBackOffPolicyFactory loadBalancedBackOffPolicyFactory() {
return new LoadBalancedBackOffPolicyFactory.NoBackOffPolicyFactory();
}
}
@Configuration
@@ -126,9 +132,10 @@ public class LoadBalancerAutoConfiguration {
public RetryLoadBalancerInterceptor ribbonInterceptor(
LoadBalancerClient loadBalancerClient, LoadBalancerRetryProperties properties,
LoadBalancedRetryPolicyFactory lbRetryPolicyFactory,
LoadBalancerRequestFactory requestFactory) {
LoadBalancerRequestFactory requestFactory,
LoadBalancedBackOffPolicyFactory backOffPolicyFactory) {
return new RetryLoadBalancerInterceptor(loadBalancerClient, properties,
lbRetryPolicyFactory, requestFactory);
lbRetryPolicyFactory, requestFactory, backOffPolicyFactory);
}
@Bean

View File

@@ -26,6 +26,8 @@ import org.springframework.http.client.ClientHttpRequestInterceptor;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.retry.RetryCallback;
import org.springframework.retry.RetryContext;
import org.springframework.retry.backoff.BackOffPolicy;
import org.springframework.retry.backoff.NoBackOffPolicy;
import org.springframework.retry.policy.NeverRetryPolicy;
import org.springframework.retry.support.RetryTemplate;
import org.springframework.util.Assert;
@@ -41,8 +43,11 @@ public class RetryLoadBalancerInterceptor implements ClientHttpRequestIntercepto
private LoadBalancerClient loadBalancer;
private LoadBalancerRetryProperties lbProperties;
private LoadBalancerRequestFactory requestFactory;
private LoadBalancedBackOffPolicyFactory backOffPolicyFactory;
@Deprecated
//TODO remove in 2.0.x
public RetryLoadBalancerInterceptor(LoadBalancerClient loadBalancer,
LoadBalancerRetryProperties lbProperties,
LoadBalancedRetryPolicyFactory lbRetryPolicyFactory,
@@ -51,6 +56,19 @@ public class RetryLoadBalancerInterceptor implements ClientHttpRequestIntercepto
this.lbRetryPolicyFactory = lbRetryPolicyFactory;
this.lbProperties = lbProperties;
this.requestFactory = requestFactory;
this.backOffPolicyFactory = new LoadBalancedBackOffPolicyFactory.NoBackOffPolicyFactory();
}
public RetryLoadBalancerInterceptor(LoadBalancerClient loadBalancer,
LoadBalancerRetryProperties lbProperties,
LoadBalancedRetryPolicyFactory lbRetryPolicyFactory,
LoadBalancerRequestFactory requestFactory,
LoadBalancedBackOffPolicyFactory backOffPolicyFactory) {
this.loadBalancer = loadBalancer;
this.lbRetryPolicyFactory = lbRetryPolicyFactory;
this.lbProperties = lbProperties;
this.requestFactory = requestFactory;
this.backOffPolicyFactory = backOffPolicyFactory;
}
@Override
@@ -62,6 +80,8 @@ public class RetryLoadBalancerInterceptor implements ClientHttpRequestIntercepto
final LoadBalancedRetryPolicy retryPolicy = lbRetryPolicyFactory.create(serviceName,
loadBalancer);
RetryTemplate template = this.retryTemplate == null ? new RetryTemplate() : this.retryTemplate;
BackOffPolicy backOffPolicy = backOffPolicyFactory.createBackOffPolicy(serviceName);
template.setBackOffPolicy(backOffPolicy == null ? new NoBackOffPolicy() : backOffPolicy);
template.setThrowLastExceptionOnExhausted(true);
template.setRetryPolicy(
!lbProperties.isEnabled() || retryPolicy == null ? new NeverRetryPolicy()

View File

@@ -2,7 +2,10 @@ package org.springframework.cloud.client.loadbalancer;
import java.util.List;
import org.junit.Test;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.http.client.ClientHttpRequestInterceptor;
import org.springframework.retry.backoff.NoBackOffPolicy;
import org.springframework.web.client.RestTemplate;
import static org.hamcrest.MatcherAssert.assertThat;
@@ -21,5 +24,13 @@ public class RetryLoadBalancerAutoConfigurationTests extends AbstractLoadBalance
ClientHttpRequestInterceptor interceptor = interceptors.get(0);
assertThat(interceptor, is(instanceOf(RetryLoadBalancerInterceptor.class)));
}
@Test
public void testDefaultBackOffPolicy() throws Exception {
ConfigurableApplicationContext context = init(OneRestTemplate.class);
LoadBalancedBackOffPolicyFactory loadBalancedBackOffPolicyFactory = context.getBean(LoadBalancedBackOffPolicyFactory.class);
assertThat(loadBalancedBackOffPolicyFactory, is(instanceOf(LoadBalancedBackOffPolicyFactory.NoBackOffPolicyFactory.class)));
assertThat(loadBalancedBackOffPolicyFactory.createBackOffPolicy("foo"), is(instanceOf(NoBackOffPolicy.class)));
}
}

View File

@@ -14,6 +14,11 @@ import org.springframework.http.HttpStatus;
import org.springframework.http.client.ClientHttpRequestExecution;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.mock.http.client.MockClientHttpResponse;
import org.springframework.retry.RetryContext;
import org.springframework.retry.backoff.BackOffContext;
import org.springframework.retry.backoff.BackOffInterruptedException;
import org.springframework.retry.backoff.BackOffPolicy;
import org.springframework.retry.backoff.ExponentialBackOffPolicy;
import org.springframework.retry.policy.NeverRetryPolicy;
import org.springframework.retry.support.RetryTemplate;
@@ -37,6 +42,7 @@ public class RetryLoadBalancerInterceptorTest {
private LoadBalancerClient client;
private LoadBalancerRetryProperties lbProperties;
private LoadBalancerRequestFactory lbRequestFactory;
private LoadBalancedBackOffPolicyFactory backOffPolicyFactory = new LoadBalancedBackOffPolicyFactory.NoBackOffPolicyFactory();
@Before
public void setUp() throws Exception {
@@ -62,7 +68,8 @@ public class RetryLoadBalancerInterceptorTest {
when(client.choose(eq("foo"))).thenReturn(serviceInstance);
when(client.execute(eq("foo"), eq(serviceInstance), any(LoadBalancerRequest.class))).thenThrow(new IOException());
lbProperties.setEnabled(false);
RetryLoadBalancerInterceptor interceptor = new RetryLoadBalancerInterceptor(client, lbProperties, lbRetryPolicyFactory, lbRequestFactory);
RetryLoadBalancerInterceptor interceptor = new RetryLoadBalancerInterceptor(client, lbProperties, lbRetryPolicyFactory,
lbRequestFactory, backOffPolicyFactory);
byte[] body = new byte[]{};
ClientHttpRequestExecution execution = mock(ClientHttpRequestExecution.class);
@@ -78,7 +85,8 @@ public class RetryLoadBalancerInterceptorTest {
when(request.getURI()).thenReturn(new URI("http://foo_underscore"));
LoadBalancedRetryPolicyFactory lbRetryPolicyFactory = mock(LoadBalancedRetryPolicyFactory.class);
lbProperties.setEnabled(true);
RetryLoadBalancerInterceptor interceptor = new RetryLoadBalancerInterceptor(client, lbProperties, lbRetryPolicyFactory, lbRequestFactory);
RetryLoadBalancerInterceptor interceptor = new RetryLoadBalancerInterceptor(client, lbProperties, lbRetryPolicyFactory, lbRequestFactory,
backOffPolicyFactory);
byte[] body = new byte[]{};
ClientHttpRequestExecution execution = mock(ClientHttpRequestExecution.class);
ClientHttpResponse rsp = interceptor.intercept(request, body, execution);
@@ -96,7 +104,8 @@ public class RetryLoadBalancerInterceptorTest {
when(client.execute(eq("foo"), eq(serviceInstance), any(LoadBalancerRequest.class))).thenReturn(clientHttpResponse);
when(this.lbRequestFactory.createRequest(any(), any(), any())).thenReturn(mock(LoadBalancerRequest.class));
lbProperties.setEnabled(true);
RetryLoadBalancerInterceptor interceptor = new RetryLoadBalancerInterceptor(client, lbProperties, lbRetryPolicyFactory, lbRequestFactory);
RetryLoadBalancerInterceptor interceptor = new RetryLoadBalancerInterceptor(client, lbProperties, lbRetryPolicyFactory,
lbRequestFactory, backOffPolicyFactory);
byte[] body = new byte[]{};
ClientHttpRequestExecution execution = mock(ClientHttpRequestExecution.class);
interceptor.intercept(request, body, execution);
@@ -117,7 +126,8 @@ public class RetryLoadBalancerInterceptorTest {
when(client.execute(eq("foo"), eq(serviceInstance), any(LoadBalancerRequest.class))).thenReturn(clientHttpResponse);
when(this.lbRequestFactory.createRequest(any(), any(), any())).thenReturn(mock(LoadBalancerRequest.class));
lbProperties.setEnabled(true);
RetryLoadBalancerInterceptor interceptor = new RetryLoadBalancerInterceptor(client, lbProperties, lbRetryPolicyFactory, lbRequestFactory);
RetryLoadBalancerInterceptor interceptor = new RetryLoadBalancerInterceptor(client, lbProperties, lbRetryPolicyFactory,
lbRequestFactory, backOffPolicyFactory);
byte[] body = new byte[]{};
ClientHttpRequestExecution execution = mock(ClientHttpRequestExecution.class);
ClientHttpResponse rsp = interceptor.intercept(request, body, execution);
@@ -143,7 +153,8 @@ public class RetryLoadBalancerInterceptorTest {
when(client.execute(eq("foo"), eq(serviceInstance), nullable(LoadBalancerRequest.class))).
thenReturn(clientHttpResponseNotFound).thenReturn(clientHttpResponseOk);
lbProperties.setEnabled(true);
RetryLoadBalancerInterceptor interceptor = new RetryLoadBalancerInterceptor(client, lbProperties, lbRetryPolicyFactory, lbRequestFactory);
RetryLoadBalancerInterceptor interceptor = new RetryLoadBalancerInterceptor(client, lbProperties, lbRetryPolicyFactory,
lbRequestFactory, backOffPolicyFactory);
byte[] body = new byte[]{};
ClientHttpRequestExecution execution = mock(ClientHttpRequestExecution.class);
ClientHttpResponse rsp = interceptor.intercept(request, body, execution);
@@ -162,18 +173,23 @@ public class RetryLoadBalancerInterceptorTest {
when(policy.canRetryNextServer(any(LoadBalancedRetryContext.class))).thenReturn(true);
LoadBalancedRetryPolicyFactory lbRetryPolicyFactory = mock(LoadBalancedRetryPolicyFactory.class);
when(lbRetryPolicyFactory.create(eq("foo"), any(ServiceInstanceChooser.class))).thenReturn(policy);
LoadBalancedBackOffPolicyFactory backOffPolicyFactory = mock(LoadBalancedBackOffPolicyFactory.class);
MyBackOffPolicy backOffPolicy = new MyBackOffPolicy();
when(backOffPolicyFactory.createBackOffPolicy(eq("foo"))).thenReturn(backOffPolicy);
ServiceInstance serviceInstance = mock(ServiceInstance.class);
when(client.choose(eq("foo"))).thenReturn(serviceInstance);
when(client.execute(eq("foo"), eq(serviceInstance), any(LoadBalancerRequest.class))).thenThrow(new IOException()).thenReturn(clientHttpResponse);
when(this.lbRequestFactory.createRequest(any(), any(), any())).thenReturn(mock(LoadBalancerRequest.class));
lbProperties.setEnabled(true);
RetryLoadBalancerInterceptor interceptor = new RetryLoadBalancerInterceptor(client, lbProperties, lbRetryPolicyFactory, lbRequestFactory);
RetryLoadBalancerInterceptor interceptor = new RetryLoadBalancerInterceptor(client, lbProperties, lbRetryPolicyFactory, lbRequestFactory,
backOffPolicyFactory);
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(clientHttpResponse));
verify(lbRequestFactory, times(2)).createRequest(request, body, execution);
assertThat(backOffPolicy.getBackoffAttempts(), is(1));
}
@Test(expected = IOException.class)
@@ -190,10 +206,35 @@ public class RetryLoadBalancerInterceptorTest {
when(client.execute(eq("foo"), eq(serviceInstance), any(LoadBalancerRequest.class))).thenThrow(new IOException()).thenReturn(clientHttpResponse);
when(this.lbRequestFactory.createRequest(any(), any(), any())).thenReturn(mock(LoadBalancerRequest.class));
lbProperties.setEnabled(true);
RetryLoadBalancerInterceptor interceptor = new RetryLoadBalancerInterceptor(client, lbProperties, lbRetryPolicyFactory, lbRequestFactory);
RetryLoadBalancerInterceptor interceptor = new RetryLoadBalancerInterceptor(client, lbProperties, lbRetryPolicyFactory,
lbRequestFactory, backOffPolicyFactory);
byte[] body = new byte[]{};
ClientHttpRequestExecution execution = mock(ClientHttpRequestExecution.class);
ClientHttpResponse rsp = interceptor.intercept(request, body, execution);
verify(lbRequestFactory).createRequest(request, body, execution);
}
class MyBackOffPolicy implements BackOffPolicy {
private int backoffAttempts = 0;
@Override
public BackOffContext start(RetryContext retryContext) {
return new BackOffContext() {
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
};
}
@Override
public void backOff(BackOffContext backOffContext) throws BackOffInterruptedException {
backoffAttempts++;
}
public int getBackoffAttempts() {
return backoffAttempts;
}
}
}

View File

@@ -12,7 +12,7 @@ public class PropertySourceBootstrapProperties {
private boolean overrideSystemProperties = true;
/**
* Flag to indicate that {@link #isSystemPropertiesOverride()
* Flag to indicate that {@link #isOverrideSystemProperties()
* systemPropertiesOverride} can be used. Set to false to prevent users from changing
* the default accidentally. Default true.
*/