Merge pull request #1375 from ryanjbaxter/simplify-retry-logic
Provide A Retry Policy For Ribbon Clients
This commit is contained in:
7
pom.xml
7
pom.xml
@@ -79,6 +79,13 @@
|
||||
<type>pom</type>
|
||||
<scope>import</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-commons</artifactId>
|
||||
<type>test-jar</type>
|
||||
<scope>test</scope>
|
||||
<version>${spring-cloud-commons.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-config-dependencies</artifactId>
|
||||
|
||||
@@ -44,6 +44,16 @@
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-aop</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.retry</groupId>
|
||||
<artifactId>spring-retry</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-commons</artifactId>
|
||||
@@ -183,6 +193,12 @@
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-commons</artifactId>
|
||||
<type>test-jar</type>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.aspectj</groupId>
|
||||
<artifactId>aspectjweaver</artifactId>
|
||||
|
||||
@@ -28,16 +28,20 @@ import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
|
||||
import org.springframework.boot.autoconfigure.condition.AnyNestedCondition;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.cloud.client.actuator.HasFeatures;
|
||||
import org.springframework.cloud.client.loadbalancer.LoadBalancedRetryPolicyFactory;
|
||||
import org.springframework.cloud.client.loadbalancer.LoadBalancerAutoConfiguration;
|
||||
import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;
|
||||
import org.springframework.cloud.client.loadbalancer.RestTemplateCustomizer;
|
||||
import org.springframework.cloud.client.loadbalancer.RetryLoadBalancerInterceptor;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Conditional;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.retry.support.RetryTemplate;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
import com.netflix.client.IClient;
|
||||
@@ -78,6 +82,12 @@ public class RibbonAutoConfiguration {
|
||||
return new RibbonLoadBalancerClient(springClientFactory());
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnClass(name = "org.springframework.retry.support.RetryTemplate")
|
||||
public LoadBalancedRetryPolicyFactory loadBalancedRetryPolicyFactory(SpringClientFactory clientFactory) {
|
||||
return new RibbonLoadBalancedRetryPolicyFactory(clientFactory);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public PropertiesFactory propertiesFactory() {
|
||||
|
||||
@@ -0,0 +1,93 @@
|
||||
/*
|
||||
* Copyright 2013-2016 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.cloud.netflix.ribbon;
|
||||
|
||||
import org.springframework.cloud.client.ServiceInstance;
|
||||
import org.springframework.cloud.client.loadbalancer.LoadBalancedRetryContext;
|
||||
import org.springframework.cloud.client.loadbalancer.LoadBalancedRetryPolicy;
|
||||
import org.springframework.cloud.client.loadbalancer.LoadBalancedRetryPolicyFactory;
|
||||
import org.springframework.cloud.client.loadbalancer.ServiceInstanceChooser;
|
||||
import org.springframework.http.HttpMethod;
|
||||
|
||||
/**
|
||||
* @author Ryan Baxter
|
||||
*/
|
||||
public class RibbonLoadBalancedRetryPolicyFactory implements LoadBalancedRetryPolicyFactory {
|
||||
|
||||
private SpringClientFactory clientFactory;
|
||||
|
||||
public RibbonLoadBalancedRetryPolicyFactory(SpringClientFactory clientFactory) {
|
||||
this.clientFactory = clientFactory;
|
||||
}
|
||||
|
||||
@Override
|
||||
public LoadBalancedRetryPolicy create(final String serviceId, final ServiceInstanceChooser loadBalanceChooser) {
|
||||
final RibbonLoadBalancerContext lbContext = this.clientFactory
|
||||
.getLoadBalancerContext(serviceId);
|
||||
return new LoadBalancedRetryPolicy() {
|
||||
private int sameServerCount = 0;
|
||||
private int nextServerCount = 0;
|
||||
private ServiceInstance lastServiceInstance = null;
|
||||
public boolean canRetry(LoadBalancedRetryContext context) {
|
||||
HttpMethod method = context.getRequest().getMethod();
|
||||
return HttpMethod.GET == method || lbContext.isOkToRetryOnAllOperations();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canRetrySameServer(LoadBalancedRetryContext context) {
|
||||
return sameServerCount < lbContext.getRetryHandler().getMaxRetriesOnSameServer() && canRetry(context);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canRetryNextServer(LoadBalancedRetryContext context) {
|
||||
//this will be called after a failure occurs and we increment the counter
|
||||
//so we check that the count is less than or equals to too make sure
|
||||
//we try the next server the right number of times
|
||||
return nextServerCount <= lbContext.getRetryHandler().getMaxRetriesOnNextServer() && canRetry(context);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close(LoadBalancedRetryContext context) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerThrowable(LoadBalancedRetryContext context, Throwable throwable) {
|
||||
//Check if we need to ask the load balancer for a new server.
|
||||
//Do this before we increment the counters because the first call to this method
|
||||
//is not a retry it is just an initial failure.
|
||||
if(!canRetrySameServer(context) && canRetryNextServer(context)) {
|
||||
context.setServiceInstance(loadBalanceChooser.choose(serviceId));
|
||||
}
|
||||
//This method is called regardless of whether we are retrying or making the first request.
|
||||
//Since we do not count the initial request in the retry count we don't reset the counter
|
||||
//until we actually equal the same server count limit. This will allow us to make the initial
|
||||
//request plus the right number of retries.
|
||||
if(sameServerCount >= lbContext.getRetryHandler().getMaxRetriesOnSameServer() && canRetry(context)) {
|
||||
//reset same server since we are moving to a new server
|
||||
sameServerCount = 0;
|
||||
nextServerCount++;
|
||||
if(!canRetryNextServer(context)) {
|
||||
context.setExhaustedOnly();
|
||||
}
|
||||
} else {
|
||||
sameServerCount++;
|
||||
}
|
||||
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -20,15 +20,12 @@ import java.io.IOException;
|
||||
import java.net.URI;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.cloud.client.DefaultServiceInstance;
|
||||
import org.springframework.cloud.client.ServiceInstance;
|
||||
import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;
|
||||
import org.springframework.cloud.client.loadbalancer.LoadBalancerRequest;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
import org.springframework.web.util.UriComponentsBuilder;
|
||||
|
||||
import com.netflix.client.config.IClientConfig;
|
||||
import com.netflix.loadbalancer.ILoadBalancer;
|
||||
import com.netflix.loadbalancer.Server;
|
||||
@@ -36,6 +33,7 @@ import com.netflix.loadbalancer.Server;
|
||||
/**
|
||||
* @author Spencer Gibb
|
||||
* @author Dave Syer
|
||||
* @author Ryan Baxter
|
||||
*/
|
||||
public class RibbonLoadBalancerClient implements LoadBalancerClient {
|
||||
|
||||
@@ -79,12 +77,25 @@ public class RibbonLoadBalancerClient implements LoadBalancerClient {
|
||||
RibbonServer ribbonServer = new RibbonServer(serviceId, server, isSecure(server,
|
||||
serviceId), serverIntrospector(serviceId).getMetadata(server));
|
||||
|
||||
return execute(serviceId, ribbonServer, request);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T execute(String serviceId, ServiceInstance serviceInstance, LoadBalancerRequest<T> request) throws IOException {
|
||||
Server server = null;
|
||||
if(serviceInstance instanceof RibbonServer) {
|
||||
server = ((RibbonServer)serviceInstance).getServer();
|
||||
}
|
||||
if (server == null) {
|
||||
throw new IllegalStateException("No instances available for " + serviceId);
|
||||
}
|
||||
|
||||
RibbonLoadBalancerContext context = this.clientFactory
|
||||
.getLoadBalancerContext(serviceId);
|
||||
RibbonStatsRecorder statsRecorder = new RibbonStatsRecorder(context, server);
|
||||
|
||||
try {
|
||||
T returnVal = request.apply(ribbonServer);
|
||||
T returnVal = request.apply(serviceInstance);
|
||||
statsRecorder.recordStats(returnVal);
|
||||
return returnVal;
|
||||
}
|
||||
|
||||
@@ -16,9 +16,9 @@
|
||||
|
||||
package org.springframework.cloud.netflix.ribbon;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
import java.net.URL;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
@@ -35,7 +35,6 @@ import org.springframework.http.client.ClientHttpResponse;
|
||||
import org.springframework.http.client.support.HttpRequestWrapper;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
import org.springframework.web.util.UriComponentsBuilder;
|
||||
|
||||
import com.netflix.loadbalancer.Server;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
@@ -105,6 +104,17 @@ public class RibbonInterceptorTests {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T execute(String s, ServiceInstance serviceInstance, LoadBalancerRequest<T> request) throws IOException {
|
||||
try {
|
||||
return request.apply(this.instance);
|
||||
}
|
||||
catch (Exception ex) {
|
||||
ReflectionUtils.rethrowRuntimeException(ex);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public URI reconstructURI(ServiceInstance instance, URI original) {
|
||||
return UriComponentsBuilder.fromUri(original).host(instance.getHost())
|
||||
|
||||
@@ -0,0 +1,214 @@
|
||||
package org.springframework.cloud.netflix.ribbon;
|
||||
|
||||
import com.netflix.client.DefaultLoadBalancerRetryHandler;
|
||||
import com.netflix.client.config.CommonClientConfigKey;
|
||||
import com.netflix.client.config.IClientConfig;
|
||||
import com.netflix.loadbalancer.BaseLoadBalancer;
|
||||
import com.netflix.loadbalancer.LoadBalancerStats;
|
||||
import com.netflix.loadbalancer.Server;
|
||||
import com.netflix.loadbalancer.ServerStats;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
import org.springframework.cloud.client.ServiceInstance;
|
||||
import org.springframework.cloud.client.loadbalancer.LoadBalancedRetryContext;
|
||||
import org.springframework.cloud.client.loadbalancer.LoadBalancedRetryPolicy;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.HttpRequest;
|
||||
import org.springframework.cloud.netflix.ribbon.RibbonLoadBalancerClient.RibbonServer;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.mockito.BDDMockito.given;
|
||||
import static org.mockito.Matchers.any;
|
||||
import static org.mockito.Matchers.anyBoolean;
|
||||
import static org.mockito.Matchers.anyInt;
|
||||
import static org.mockito.Matchers.anyObject;
|
||||
import static org.mockito.Matchers.eq;
|
||||
import static org.mockito.Mockito.anyString;
|
||||
import static org.mockito.Mockito.doReturn;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.spy;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
|
||||
/**
|
||||
* @author Ryan Baxter
|
||||
*/
|
||||
public class RibbonLoadBalancedRetryPolicyFactoryTest {
|
||||
|
||||
@Mock
|
||||
private SpringClientFactory clientFactory;
|
||||
|
||||
@Mock
|
||||
private BaseLoadBalancer loadBalancer;
|
||||
|
||||
@Mock
|
||||
private LoadBalancerStats loadBalancerStats;
|
||||
|
||||
@Mock
|
||||
private ServerStats serverStats;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
given(this.clientFactory.getLoadBalancerContext(anyString())).willReturn(
|
||||
new RibbonLoadBalancerContext(this.loadBalancer));
|
||||
given(this.clientFactory.getInstance(anyString(), eq(ServerIntrospector.class)))
|
||||
.willReturn(new DefaultServerIntrospector() {
|
||||
@Override
|
||||
public Map<String, String> getMetadata(Server server) {
|
||||
return Collections.singletonMap("mykey", "myvalue");
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() throws Exception {}
|
||||
|
||||
@Test
|
||||
public void testGetRetryPolicyNoRetry() throws Exception {
|
||||
int sameServer = 0;
|
||||
int nextServer = 0;
|
||||
boolean retryOnAllOps = false;
|
||||
RibbonServer server = getRibbonServer();
|
||||
IClientConfig config = mock(IClientConfig.class);
|
||||
doReturn(sameServer).when(config).get(eq(CommonClientConfigKey.MaxAutoRetries), anyInt());
|
||||
doReturn(sameServer).when(config).getPropertyAsInteger(eq(CommonClientConfigKey.MaxAutoRetries), anyInt());
|
||||
doReturn(nextServer).when(config).get(eq(CommonClientConfigKey.MaxAutoRetriesNextServer), anyInt());
|
||||
doReturn(nextServer).when(config).getPropertyAsInteger(eq(CommonClientConfigKey.MaxAutoRetriesNextServer), anyInt());
|
||||
doReturn(retryOnAllOps).when(config).get(eq(CommonClientConfigKey.OkToRetryOnAllOperations), anyBoolean());
|
||||
doReturn(retryOnAllOps).when(config).getPropertyAsBoolean(eq(CommonClientConfigKey.OkToRetryOnAllOperations), anyBoolean());
|
||||
doReturn(server.getServiceId()).when(config).getClientName();
|
||||
doReturn(config).when(clientFactory).getClientConfig(eq(server.getServiceId()));
|
||||
clientFactory.getLoadBalancerContext(server.getServiceId()).setRetryHandler(new DefaultLoadBalancerRetryHandler(config));
|
||||
RibbonLoadBalancerClient client = getRibbonLoadBalancerClient(server);
|
||||
RibbonLoadBalancedRetryPolicyFactory factory = new RibbonLoadBalancedRetryPolicyFactory(clientFactory);
|
||||
LoadBalancedRetryPolicy policy = factory.create(server.getServiceId(), client);
|
||||
HttpRequest request = mock(HttpRequest.class);
|
||||
doReturn(HttpMethod.GET).when(request).getMethod();
|
||||
LoadBalancedRetryContext context = new LoadBalancedRetryContext(null, request);
|
||||
assertThat(policy.canRetryNextServer(context), is(true));
|
||||
assertThat(policy.canRetrySameServer(context), is(false));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetRetryPolicyNotGet() throws Exception {
|
||||
int sameServer = 3;
|
||||
int nextServer = 3;
|
||||
boolean retryOnAllOps = false;
|
||||
RibbonServer server = getRibbonServer();
|
||||
IClientConfig config = mock(IClientConfig.class);
|
||||
doReturn(sameServer).when(config).get(eq(CommonClientConfigKey.MaxAutoRetries), anyInt());
|
||||
doReturn(sameServer).when(config).getPropertyAsInteger(eq(CommonClientConfigKey.MaxAutoRetries), anyInt());
|
||||
doReturn(nextServer).when(config).get(eq(CommonClientConfigKey.MaxAutoRetriesNextServer), anyInt());
|
||||
doReturn(nextServer).when(config).getPropertyAsInteger(eq(CommonClientConfigKey.MaxAutoRetriesNextServer), anyInt());
|
||||
doReturn(retryOnAllOps).when(config).get(eq(CommonClientConfigKey.OkToRetryOnAllOperations), anyBoolean());
|
||||
doReturn(retryOnAllOps).when(config).getPropertyAsBoolean(eq(CommonClientConfigKey.OkToRetryOnAllOperations), anyBoolean());
|
||||
doReturn(server.getServiceId()).when(config).getClientName();
|
||||
doReturn(config).when(clientFactory).getClientConfig(eq(server.getServiceId()));
|
||||
clientFactory.getLoadBalancerContext(server.getServiceId()).setRetryHandler(new DefaultLoadBalancerRetryHandler(config));
|
||||
RibbonLoadBalancerClient client = getRibbonLoadBalancerClient(server);
|
||||
RibbonLoadBalancedRetryPolicyFactory factory = new RibbonLoadBalancedRetryPolicyFactory(clientFactory);
|
||||
LoadBalancedRetryPolicy policy = factory.create(server.getServiceId(), client);
|
||||
HttpRequest request = mock(HttpRequest.class);
|
||||
doReturn(HttpMethod.POST).when(request).getMethod();
|
||||
LoadBalancedRetryContext context = new LoadBalancedRetryContext(null, request);
|
||||
assertThat(policy.canRetryNextServer(context), is(false));
|
||||
assertThat(policy.canRetrySameServer(context), is(false));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetRetryPolicyRetryOnNonGet() throws Exception {
|
||||
int sameServer = 3;
|
||||
int nextServer = 3;
|
||||
boolean retryOnAllOps = true;
|
||||
RibbonServer server = getRibbonServer();
|
||||
IClientConfig config = mock(IClientConfig.class);
|
||||
doReturn(sameServer).when(config).get(eq(CommonClientConfigKey.MaxAutoRetries), anyInt());
|
||||
doReturn(sameServer).when(config).getPropertyAsInteger(eq(CommonClientConfigKey.MaxAutoRetries), anyInt());
|
||||
doReturn(nextServer).when(config).get(eq(CommonClientConfigKey.MaxAutoRetriesNextServer), anyInt());
|
||||
doReturn(nextServer).when(config).getPropertyAsInteger(eq(CommonClientConfigKey.MaxAutoRetriesNextServer), anyInt());
|
||||
doReturn(retryOnAllOps).when(config).get(eq(CommonClientConfigKey.OkToRetryOnAllOperations), anyBoolean());
|
||||
doReturn(retryOnAllOps).when(config).getPropertyAsBoolean(eq(CommonClientConfigKey.OkToRetryOnAllOperations), anyBoolean());
|
||||
doReturn(server.getServiceId()).when(config).getClientName();
|
||||
doReturn(config).when(clientFactory).getClientConfig(eq(server.getServiceId()));
|
||||
clientFactory.getLoadBalancerContext(server.getServiceId()).initWithNiwsConfig(config);
|
||||
RibbonLoadBalancerClient client = getRibbonLoadBalancerClient(server);
|
||||
RibbonLoadBalancedRetryPolicyFactory factory = new RibbonLoadBalancedRetryPolicyFactory(clientFactory);
|
||||
LoadBalancedRetryPolicy policy = factory.create(server.getServiceId(), client);
|
||||
HttpRequest request = mock(HttpRequest.class);
|
||||
doReturn(HttpMethod.POST).when(request).getMethod();
|
||||
LoadBalancedRetryContext context = new LoadBalancedRetryContext(null, request);
|
||||
assertThat(policy.canRetryNextServer(context), is(true));
|
||||
assertThat(policy.canRetrySameServer(context), is(true));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetRetryPolicyRetryCount() throws Exception {
|
||||
int sameServer = 3;
|
||||
int nextServer = 3;
|
||||
RibbonServer server = getRibbonServer();
|
||||
IClientConfig config = mock(IClientConfig.class);
|
||||
doReturn(sameServer).when(config).get(eq(CommonClientConfigKey.MaxAutoRetries), anyInt());
|
||||
doReturn(nextServer).when(config).get(eq(CommonClientConfigKey.MaxAutoRetriesNextServer), anyInt());
|
||||
doReturn(false).when(config).get(eq(CommonClientConfigKey.OkToRetryOnAllOperations), eq(false));
|
||||
doReturn(config).when(clientFactory).getClientConfig(eq(server.getServiceId()));
|
||||
clientFactory.getLoadBalancerContext(server.getServiceId()).setRetryHandler(new DefaultLoadBalancerRetryHandler(config));
|
||||
RibbonLoadBalancerClient client = getRibbonLoadBalancerClient(server);
|
||||
RibbonLoadBalancedRetryPolicyFactory factory = new RibbonLoadBalancedRetryPolicyFactory(clientFactory);
|
||||
LoadBalancedRetryPolicy policy = factory.create(server.getServiceId(), client);
|
||||
HttpRequest request = mock(HttpRequest.class);
|
||||
doReturn(HttpMethod.GET).when(request).getMethod();
|
||||
LoadBalancedRetryContext context = spy(new LoadBalancedRetryContext(null, request));
|
||||
//Loop through as if we are retrying a request until we exhaust the number of retries
|
||||
//outer loop is for next server retries
|
||||
//inner loop is for same server retries
|
||||
for(int i = 0; i < nextServer + 1; i++) {
|
||||
//iterate once time beyond the same server retry limit to cause us to reset
|
||||
//the same sever counter and increment the next server counter
|
||||
for(int j = 0; j < sameServer + 1; j++) {
|
||||
if(j < 3) {
|
||||
assertThat(policy.canRetrySameServer(context), is(true));
|
||||
} else {
|
||||
assertThat(policy.canRetrySameServer(context), is(false));
|
||||
}
|
||||
policy.registerThrowable(context, new IOException());
|
||||
}
|
||||
if(i < 3) {
|
||||
assertThat(policy.canRetryNextServer(context), is(true));
|
||||
} else {
|
||||
assertThat(policy.canRetryNextServer(context), is(false));
|
||||
}
|
||||
}
|
||||
assertThat(context.isExhaustedOnly(), is(true));
|
||||
verify(context, times(4)).setServiceInstance(any(ServiceInstance.class));
|
||||
}
|
||||
|
||||
protected RibbonLoadBalancerClient getRibbonLoadBalancerClient(
|
||||
RibbonServer ribbonServer) {
|
||||
given(this.loadBalancer.getName()).willReturn(ribbonServer.getServiceId());
|
||||
given(this.loadBalancer.chooseServer(anyObject())).willReturn(
|
||||
ribbonServer.getServer());
|
||||
given(this.loadBalancer.getLoadBalancerStats())
|
||||
.willReturn(this.loadBalancerStats);
|
||||
given(this.loadBalancerStats.getSingleServerStat(ribbonServer.getServer()))
|
||||
.willReturn(this.serverStats);
|
||||
given(this.clientFactory.getLoadBalancer(this.loadBalancer.getName()))
|
||||
.willReturn(this.loadBalancer);
|
||||
return new RibbonLoadBalancerClient(this.clientFactory);
|
||||
}
|
||||
|
||||
protected RibbonServer getRibbonServer() {
|
||||
return new RibbonServer("testService", new Server("myhost", 9080), false,
|
||||
Collections.singletonMap("mykey", "myvalue"));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
*
|
||||
* * Copyright 2013-2016 the original author or authors.
|
||||
* *
|
||||
* * Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* * you may not use this file except in compliance with the License.
|
||||
* * You may obtain a copy of the License at
|
||||
* *
|
||||
* * http://www.apache.org/licenses/LICENSE-2.0
|
||||
* *
|
||||
* * Unless required by applicable law or agreed to in writing, software
|
||||
* * distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* * See the License for the specific language governing permissions and
|
||||
* * limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.netflix.ribbon;
|
||||
|
||||
import java.util.Map;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.boot.builder.SpringApplicationBuilder;
|
||||
import org.springframework.cloud.ClassPathExclusions;
|
||||
import org.springframework.cloud.FilteredClassPathRunner;
|
||||
import org.springframework.cloud.client.loadbalancer.LoadBalancedRetryPolicyFactory;
|
||||
import org.springframework.cloud.client.loadbalancer.LoadBalancerAutoConfiguration;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.hamcrest.Matchers.hasSize;
|
||||
|
||||
/**
|
||||
* @author Ryan Baxter
|
||||
*/
|
||||
@RunWith(FilteredClassPathRunner.class)
|
||||
@ClassPathExclusions({"spring-retry-*.jar", "spring-boot-starter-aop-*.jar"})
|
||||
public class SpringRetryDisabledTests {
|
||||
|
||||
private ConfigurableApplicationContext context;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
context = new SpringApplicationBuilder().web(false)
|
||||
.sources(RibbonAutoConfiguration.class,LoadBalancerAutoConfiguration.class).run();
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() {
|
||||
if(context != null) {
|
||||
context.close();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLoadBalancedRetryFactoryBean() throws Exception {
|
||||
Map<String, LoadBalancedRetryPolicyFactory> factories = context.getBeansOfType(LoadBalancedRetryPolicyFactory.class);
|
||||
assertThat(factories.values(), hasSize(0));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
*
|
||||
* * Copyright 2013-2016 the original author or authors.
|
||||
* *
|
||||
* * Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* * you may not use this file except in compliance with the License.
|
||||
* * You may obtain a copy of the License at
|
||||
* *
|
||||
* * http://www.apache.org/licenses/LICENSE-2.0
|
||||
* *
|
||||
* * Unless required by applicable law or agreed to in writing, software
|
||||
* * distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* * See the License for the specific language governing permissions and
|
||||
* * limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.netflix.ribbon;
|
||||
|
||||
import java.util.Map;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.cloud.client.loadbalancer.LoadBalancedRetryPolicyFactory;
|
||||
import org.springframework.cloud.client.loadbalancer.LoadBalancerAutoConfiguration;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationContextAware;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.hamcrest.Matchers.instanceOf;
|
||||
import static org.hamcrest.collection.IsCollectionWithSize.hasSize;
|
||||
|
||||
/**
|
||||
* @author Ryan Baxter
|
||||
*/
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@ContextConfiguration(classes = {RibbonAutoConfiguration.class, LoadBalancerAutoConfiguration.class})
|
||||
public class SpringRetryEnabledTests implements ApplicationContextAware {
|
||||
|
||||
private ApplicationContext context;
|
||||
|
||||
@Test
|
||||
public void testLoadBalancedRetryFactoryBean() throws Exception {
|
||||
Map<String, LoadBalancedRetryPolicyFactory> factories = context.getBeansOfType(LoadBalancedRetryPolicyFactory.class);
|
||||
assertThat(factories.values(), hasSize(1));
|
||||
assertThat(factories.values().toArray()[0], instanceOf(RibbonLoadBalancedRetryPolicyFactory.class));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setApplicationContext(ApplicationContext context) throws BeansException {
|
||||
this.context = context;
|
||||
}
|
||||
}
|
||||
@@ -20,6 +20,14 @@
|
||||
<main.basedir>${basedir}/../..</main.basedir>
|
||||
</properties>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-aop</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.retry</groupId>
|
||||
<artifactId>spring-retry</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter</artifactId>
|
||||
|
||||
Reference in New Issue
Block a user