diff --git a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/feign/FeignClientsConfiguration.java b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/feign/FeignClientsConfiguration.java index 606cbbb5..27d4866d 100644 --- a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/feign/FeignClientsConfiguration.java +++ b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/feign/FeignClientsConfiguration.java @@ -41,6 +41,7 @@ import com.netflix.hystrix.HystrixCommand; import feign.Contract; import feign.Feign; import feign.Logger; +import feign.Retryer; import feign.codec.Decoder; import feign.codec.Encoder; import feign.hystrix.HystrixFeign; @@ -107,7 +108,7 @@ public class FeignClientsConfiguration { @Scope("prototype") @ConditionalOnMissingBean public Feign.Builder feignBuilder() { - return Feign.builder(); + return Feign.builder().retryer(Retryer.NEVER_RETRY); } @Bean diff --git a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/feign/ribbon/CachingSpringLoadBalancerFactory.java b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/feign/ribbon/CachingSpringLoadBalancerFactory.java index 295f507a..c88a551c 100644 --- a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/feign/ribbon/CachingSpringLoadBalancerFactory.java +++ b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/feign/ribbon/CachingSpringLoadBalancerFactory.java @@ -16,14 +16,15 @@ package org.springframework.cloud.netflix.feign.ribbon; -import java.util.Map; - -import org.springframework.cloud.netflix.ribbon.ServerIntrospector; -import org.springframework.cloud.netflix.ribbon.SpringClientFactory; -import org.springframework.util.ConcurrentReferenceHashMap; - import com.netflix.client.config.IClientConfig; import com.netflix.loadbalancer.ILoadBalancer; +import org.springframework.cloud.client.loadbalancer.LoadBalancedRetryPolicyFactory; +import org.springframework.cloud.netflix.ribbon.ServerIntrospector; +import org.springframework.cloud.netflix.ribbon.SpringClientFactory; +import org.springframework.retry.support.RetryTemplate; +import org.springframework.util.ConcurrentReferenceHashMap; + +import java.util.Map; /** * Factory for SpringLoadBalancer instances that caches the entries created. @@ -34,11 +35,16 @@ import com.netflix.loadbalancer.ILoadBalancer; public class CachingSpringLoadBalancerFactory { private final SpringClientFactory factory; + private final RetryTemplate retryTemplate; + private final LoadBalancedRetryPolicyFactory loadBalancedRetryPolicyFactory; private volatile Map cache = new ConcurrentReferenceHashMap<>(); - public CachingSpringLoadBalancerFactory(SpringClientFactory factory) { + public CachingSpringLoadBalancerFactory(SpringClientFactory factory, RetryTemplate retryTemplate, + LoadBalancedRetryPolicyFactory loadBalancedRetryPolicyFactory) { this.factory = factory; + this.retryTemplate = retryTemplate; + this.loadBalancedRetryPolicyFactory = loadBalancedRetryPolicyFactory; } public FeignLoadBalancer create(String clientName) { @@ -48,7 +54,8 @@ public class CachingSpringLoadBalancerFactory { IClientConfig config = this.factory.getClientConfig(clientName); ILoadBalancer lb = this.factory.getLoadBalancer(clientName); ServerIntrospector serverIntrospector = this.factory.getInstance(clientName, ServerIntrospector.class); - FeignLoadBalancer client = new FeignLoadBalancer(lb, config, serverIntrospector); + FeignLoadBalancer client = new FeignLoadBalancer(lb, config, serverIntrospector, retryTemplate, + loadBalancedRetryPolicyFactory); this.cache.put(clientName, client); return client; } diff --git a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/feign/ribbon/FeignLoadBalancer.java b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/feign/ribbon/FeignLoadBalancer.java index 1c3ed45a..7c613f7e 100644 --- a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/feign/ribbon/FeignLoadBalancer.java +++ b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/feign/ribbon/FeignLoadBalancer.java @@ -16,44 +16,65 @@ package org.springframework.cloud.netflix.feign.ribbon; -import java.io.IOException; -import java.net.URI; -import java.util.Collection; -import java.util.LinkedHashMap; -import java.util.Map; - -import org.springframework.cloud.netflix.ribbon.ServerIntrospector; - import com.netflix.client.AbstractLoadBalancerAwareClient; import com.netflix.client.ClientException; import com.netflix.client.ClientRequest; +import com.netflix.client.DefaultLoadBalancerRetryHandler; import com.netflix.client.IResponse; import com.netflix.client.RequestSpecificRetryHandler; -import com.netflix.client.RetryHandler; import com.netflix.client.config.CommonClientConfigKey; import com.netflix.client.config.IClientConfig; import com.netflix.loadbalancer.ILoadBalancer; import com.netflix.loadbalancer.Server; - import feign.Client; import feign.Request; import feign.Response; import feign.Util; +import org.springframework.cloud.client.ServiceInstance; +import org.springframework.cloud.client.loadbalancer.InterceptorRetryPolicy; +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.cloud.netflix.ribbon.RibbonLoadBalancerClient; +import org.springframework.cloud.netflix.ribbon.ServerIntrospector; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpMethod; +import org.springframework.http.HttpRequest; +import org.springframework.retry.RetryCallback; +import org.springframework.retry.RetryContext; +import org.springframework.retry.policy.NeverRetryPolicy; +import org.springframework.retry.support.RetryTemplate; + +import java.io.IOException; +import java.net.URI; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; import static org.springframework.cloud.netflix.ribbon.RibbonUtils.updateToHttpsIfNeeded; public class FeignLoadBalancer extends - AbstractLoadBalancerAwareClient { + AbstractLoadBalancerAwareClient implements + ServiceInstanceChooser { private final int connectTimeout; private final int readTimeout; private final IClientConfig clientConfig; private final ServerIntrospector serverIntrospector; + private final RetryTemplate retryTemplate; + private final LoadBalancedRetryPolicyFactory loadBalancedRetryPolicyFactory; public FeignLoadBalancer(ILoadBalancer lb, IClientConfig clientConfig, - ServerIntrospector serverIntrospector) { + ServerIntrospector serverIntrospector, RetryTemplate retryTemplate, + LoadBalancedRetryPolicyFactory loadBalancedRetryPolicyFactory) { super(lb, clientConfig); - this.setRetryHandler(RetryHandler.DEFAULT); + this.retryTemplate = retryTemplate; + this.loadBalancedRetryPolicyFactory = loadBalancedRetryPolicyFactory; + this.setRetryHandler(new DefaultLoadBalancerRetryHandler(clientConfig)); this.clientConfig = clientConfig; this.connectTimeout = clientConfig.get(CommonClientConfigKey.ConnectTimeout); this.readTimeout = clientConfig.get(CommonClientConfigKey.ReadTimeout); @@ -61,9 +82,9 @@ public class FeignLoadBalancer extends } @Override - public RibbonResponse execute(RibbonRequest request, IClientConfig configOverride) + public RibbonResponse execute(final RibbonRequest request, IClientConfig configOverride) throws IOException { - Request.Options options; + final Request.Options options; if (configOverride != null) { options = new Request.Options( configOverride.get(CommonClientConfigKey.ConnectTimeout, @@ -74,26 +95,45 @@ public class FeignLoadBalancer extends else { options = new Request.Options(this.connectTimeout, this.readTimeout); } - Response response = request.client().execute(request.toRequest(), options); - return new RibbonResponse(request.getUri(), response); + LoadBalancedRetryPolicy retryPolicy = loadBalancedRetryPolicyFactory.create(this.getClientName(), this); + retryTemplate.setRetryPolicy(retryPolicy == null ? new NeverRetryPolicy() + : new InterceptorRetryPolicy(request.toHttpRequest(), retryPolicy, this, this.getClientName())); + return retryTemplate.execute(new RetryCallback() { + @Override + public RibbonResponse doWithRetry(RetryContext retryContext) throws IOException { + Request feignRequest = null; + if(retryContext instanceof LoadBalancedRetryContext) { + ServiceInstance service = ((LoadBalancedRetryContext)retryContext).getServiceInstance(); + if(service != null) { + feignRequest = ((RibbonRequest)request.replaceUri(reconstructURIWithServer(new Server(service.getHost(), service.getPort()), request.getUri()))).toRequest(); + } + } + if(feignRequest == null) { + feignRequest = request.toRequest(); + } + Response response = request.client().execute(feignRequest, options); + return new RibbonResponse(request.getUri(), response); + } + }); } @Override public RequestSpecificRetryHandler getRequestSpecificRetryHandler( RibbonRequest request, IClientConfig requestConfig) { - if (this.clientConfig.get(CommonClientConfigKey.OkToRetryOnAllOperations, - false)) { - return new RequestSpecificRetryHandler(true, true, this.getRetryHandler(), - requestConfig); - } - if (!request.toRequest().method().equals("GET")) { - return new RequestSpecificRetryHandler(true, false, this.getRetryHandler(), - requestConfig); - } - else { - return new RequestSpecificRetryHandler(true, true, this.getRetryHandler(), - requestConfig); - } +// if (this.clientConfig.get(CommonClientConfigKey.OkToRetryOnAllOperations, +// false)) { +// return new RequestSpecificRetryHandler(true, true, this.getRetryHandler(), +// requestConfig); +// } +// if (!request.toRequest().method().equals("GET")) { +// return new RequestSpecificRetryHandler(true, false, this.getRetryHandler(), +// requestConfig); +// } +// else { +// return new RequestSpecificRetryHandler(true, true, this.getRetryHandler(), +// requestConfig); +// } + return new RequestSpecificRetryHandler(false, false, this.getRetryHandler(), requestConfig); } @Override @@ -102,6 +142,12 @@ public class FeignLoadBalancer extends return super.reconstructURIWithServer(server, uri); } + @Override + public ServiceInstance choose(String serviceId) { + return new RibbonLoadBalancerClient.RibbonServer(serviceId, + this.getLoadBalancer().chooseServer(serviceId)); + } + static class RibbonRequest extends ClientRequest implements Cloneable { private final Request request; @@ -129,6 +175,33 @@ public class FeignLoadBalancer extends return this.client; } + HttpRequest toHttpRequest() { + return new HttpRequest() { + @Override + public HttpMethod getMethod() { + return HttpMethod.resolve(RibbonRequest.this.toRequest().method()); + } + + @Override + public URI getURI() { + return RibbonRequest.this.getUri(); + } + + @Override + public HttpHeaders getHeaders() { + Map> headers = new HashMap>(); + Map> feignHeaders = RibbonRequest.this.toRequest().headers(); + for(String key : feignHeaders.keySet()) { + headers.put(key, new ArrayList(feignHeaders.get(key))); + } + HttpHeaders httpHeaders = new HttpHeaders(); + httpHeaders.putAll(headers); + return httpHeaders; + + } + }; + } + @Override public Object clone() { return new RibbonRequest(this.client, this.request, getUri()); diff --git a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/feign/ribbon/FeignRibbonClientAutoConfiguration.java b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/feign/ribbon/FeignRibbonClientAutoConfiguration.java index d1d8c821..2a29a252 100644 --- a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/feign/ribbon/FeignRibbonClientAutoConfiguration.java +++ b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/feign/ribbon/FeignRibbonClientAutoConfiguration.java @@ -22,11 +22,13 @@ import org.springframework.boot.autoconfigure.AutoConfigureBefore; 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.loadbalancer.LoadBalancedRetryPolicyFactory; import org.springframework.cloud.netflix.feign.FeignAutoConfiguration; import org.springframework.cloud.netflix.ribbon.SpringClientFactory; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Primary; +import org.springframework.retry.support.RetryTemplate; import com.netflix.loadbalancer.ILoadBalancer; @@ -50,8 +52,16 @@ public class FeignRibbonClientAutoConfiguration { @Bean @Primary public CachingSpringLoadBalancerFactory cachingLBClientFactory( - SpringClientFactory factory) { - return new CachingSpringLoadBalancerFactory(factory); + SpringClientFactory factory, LoadBalancedRetryPolicyFactory retryPolicyFactory, + RetryTemplate retryTemplate) { + return new CachingSpringLoadBalancerFactory(factory, retryTemplate, retryPolicyFactory); + } + + @Bean + public RetryTemplate retryTemplate() { + RetryTemplate template = new RetryTemplate(); + template.setThrowLastExceptionOnExhausted(true); + return template; } @Bean diff --git a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/ribbon/RibbonLoadBalancerClient.java b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/ribbon/RibbonLoadBalancerClient.java index 414e8674..000e7557 100644 --- a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/ribbon/RibbonLoadBalancerClient.java +++ b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/ribbon/RibbonLoadBalancerClient.java @@ -141,17 +141,17 @@ public class RibbonLoadBalancerClient implements LoadBalancerClient { return this.clientFactory.getLoadBalancer(serviceId); } - protected static class RibbonServer implements ServiceInstance { + public static class RibbonServer implements ServiceInstance { private final String serviceId; private final Server server; private final boolean secure; private Map metadata; - protected RibbonServer(String serviceId, Server server) { + public RibbonServer(String serviceId, Server server) { this(serviceId, server, false, Collections. emptyMap()); } - protected RibbonServer(String serviceId, Server server, boolean secure, + public RibbonServer(String serviceId, Server server, boolean secure, Map metadata) { this.serviceId = serviceId; this.server = server; diff --git a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/ribbon/apache/RibbonLoadBalancingHttpClient.java b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/ribbon/apache/RibbonLoadBalancingHttpClient.java index 10fa59aa..56f021ea 100644 --- a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/ribbon/apache/RibbonLoadBalancingHttpClient.java +++ b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/ribbon/apache/RibbonLoadBalancingHttpClient.java @@ -16,8 +16,12 @@ package org.springframework.cloud.netflix.ribbon.apache; -import java.net.URI; - +import com.netflix.client.RequestSpecificRetryHandler; +import com.netflix.client.RetryHandler; +import com.netflix.client.config.CommonClientConfigKey; +import com.netflix.client.config.IClientConfig; +import com.netflix.loadbalancer.ILoadBalancer; +import com.netflix.loadbalancer.Server; import org.apache.http.HttpResponse; import org.apache.http.client.HttpClient; import org.apache.http.client.config.RequestConfig; @@ -27,10 +31,7 @@ import org.springframework.cloud.netflix.ribbon.ServerIntrospector; import org.springframework.cloud.netflix.ribbon.support.AbstractLoadBalancingClient; import org.springframework.web.util.UriComponentsBuilder; -import com.netflix.client.config.CommonClientConfigKey; -import com.netflix.client.config.IClientConfig; -import com.netflix.loadbalancer.ILoadBalancer; -import com.netflix.loadbalancer.Server; +import java.net.URI; import static org.springframework.cloud.netflix.ribbon.RibbonUtils.updateToHttpsIfNeeded; @@ -102,4 +103,9 @@ public class RibbonLoadBalancingHttpClient return super.reconstructURIWithServer(server, uri); } + @Override + public RequestSpecificRetryHandler getRequestSpecificRetryHandler(RibbonApacheHttpRequest request, IClientConfig requestConfig) { + return new RequestSpecificRetryHandler(false, false, RetryHandler.DEFAULT, null); + } + } diff --git a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/zuul/filters/ZuulProperties.java b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/zuul/filters/ZuulProperties.java index 54bebeb5..687f18e6 100644 --- a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/zuul/filters/ZuulProperties.java +++ b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/zuul/filters/ZuulProperties.java @@ -16,6 +16,15 @@ package org.springframework.cloud.netflix.zuul.filters; +import com.netflix.hystrix.HystrixCommandProperties.ExecutionIsolationStrategy; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.util.ClassUtils; +import org.springframework.util.StringUtils; + +import javax.annotation.PostConstruct; import java.util.Arrays; import java.util.Collections; import java.util.LinkedHashMap; @@ -25,18 +34,6 @@ import java.util.Map; import java.util.Map.Entry; import java.util.Set; -import javax.annotation.PostConstruct; - -import org.springframework.boot.context.properties.ConfigurationProperties; -import org.springframework.util.ClassUtils; -import org.springframework.util.StringUtils; - -import com.netflix.hystrix.HystrixCommandProperties.ExecutionIsolationStrategy; - -import lombok.AllArgsConstructor; -import lombok.Data; -import lombok.NoArgsConstructor; - import static com.netflix.hystrix.HystrixCommandProperties.ExecutionIsolationStrategy.SEMAPHORE; /** @@ -71,7 +68,7 @@ public class ZuulProperties { * Flag for whether retry is supported by default (assuming the routes themselves * support it). */ - private Boolean retryable; + private Boolean retryable = false; /** * Map of route names to properties. diff --git a/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/feign/ribbon/CachingSpringLoadBalancerFactoryTests.java b/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/feign/ribbon/CachingSpringLoadBalancerFactoryTests.java index 5594f0b4..fa714a16 100644 --- a/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/feign/ribbon/CachingSpringLoadBalancerFactoryTests.java +++ b/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/feign/ribbon/CachingSpringLoadBalancerFactoryTests.java @@ -16,20 +16,21 @@ package org.springframework.cloud.netflix.feign.ribbon; -import static org.junit.Assert.assertNotNull; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - +import com.netflix.client.config.CommonClientConfigKey; +import com.netflix.client.config.DefaultClientConfigImpl; +import com.netflix.client.config.IClientConfig; import org.junit.Before; import org.junit.Test; import org.mockito.Mock; import org.mockito.MockitoAnnotations; +import org.springframework.cloud.netflix.ribbon.RibbonLoadBalancedRetryPolicyFactory; import org.springframework.cloud.netflix.ribbon.SpringClientFactory; +import org.springframework.retry.support.RetryTemplate; -import com.netflix.client.config.CommonClientConfigKey; -import com.netflix.client.config.DefaultClientConfigImpl; -import com.netflix.client.config.IClientConfig; +import static org.junit.Assert.assertNotNull; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; /** * @author Spencer Gibb @@ -39,6 +40,9 @@ public class CachingSpringLoadBalancerFactoryTests { @Mock private SpringClientFactory delegate; + @Mock + private RibbonLoadBalancedRetryPolicyFactory loadBalancedRetryPolicyFactory; + private CachingSpringLoadBalancerFactory factory; @Before @@ -52,7 +56,8 @@ public class CachingSpringLoadBalancerFactoryTests { when(this.delegate.getClientConfig("client1")).thenReturn(config); when(this.delegate.getClientConfig("client2")).thenReturn(config); - this.factory = new CachingSpringLoadBalancerFactory(this.delegate); + this.factory = new CachingSpringLoadBalancerFactory(this.delegate, new RetryTemplate(), + loadBalancedRetryPolicyFactory); } @Test diff --git a/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/feign/ribbon/FeignLoadBalancerTests.java b/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/feign/ribbon/FeignLoadBalancerTests.java index e8d52c12..5643b6a2 100644 --- a/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/feign/ribbon/FeignLoadBalancerTests.java +++ b/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/feign/ribbon/FeignLoadBalancerTests.java @@ -1,5 +1,32 @@ package org.springframework.cloud.netflix.feign.ribbon; +import com.netflix.client.config.IClientConfig; +import com.netflix.loadbalancer.ILoadBalancer; +import com.netflix.loadbalancer.Server; +import feign.Client; +import feign.Request; +import feign.Request.Options; +import feign.RequestTemplate; +import feign.Response; +import lombok.SneakyThrows; +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.MockitoAnnotations; +import org.springframework.cloud.netflix.feign.ribbon.FeignLoadBalancer.RibbonRequest; +import org.springframework.cloud.netflix.feign.ribbon.FeignLoadBalancer.RibbonResponse; +import org.springframework.cloud.netflix.ribbon.DefaultServerIntrospector; +import org.springframework.cloud.netflix.ribbon.RibbonLoadBalancedRetryPolicyFactory; +import org.springframework.cloud.netflix.ribbon.ServerIntrospector; +import org.springframework.retry.support.RetryTemplate; + +import java.net.URI; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + import static com.netflix.client.config.CommonClientConfigKey.ConnectTimeout; import static com.netflix.client.config.CommonClientConfigKey.IsSecure; import static com.netflix.client.config.CommonClientConfigKey.MaxAutoRetries; @@ -15,33 +42,6 @@ import static org.mockito.Matchers.anyBoolean; import static org.mockito.Matchers.eq; import static org.mockito.Mockito.when; -import java.net.URI; -import java.util.Collection; -import java.util.Collections; -import java.util.HashMap; -import java.util.Map; - -import org.junit.Before; -import org.junit.Test; -import org.mockito.Mock; -import org.mockito.Mockito; -import org.mockito.MockitoAnnotations; -import org.springframework.cloud.netflix.feign.ribbon.FeignLoadBalancer.RibbonRequest; -import org.springframework.cloud.netflix.feign.ribbon.FeignLoadBalancer.RibbonResponse; -import org.springframework.cloud.netflix.ribbon.DefaultServerIntrospector; -import org.springframework.cloud.netflix.ribbon.ServerIntrospector; - -import com.netflix.client.config.IClientConfig; -import com.netflix.loadbalancer.ILoadBalancer; -import com.netflix.loadbalancer.Server; - -import feign.Client; -import feign.Request; -import feign.Request.Options; -import feign.RequestTemplate; -import feign.Response; -import lombok.SneakyThrows; - public class FeignLoadBalancerTests { @Mock @@ -50,6 +50,9 @@ public class FeignLoadBalancerTests { private ILoadBalancer lb; @Mock private IClientConfig config; + @Mock + private RibbonLoadBalancedRetryPolicyFactory loadBalancedRetryPolicyFactory; + private RetryTemplate retryTemplate; private FeignLoadBalancer feignLoadBalancer; @@ -68,6 +71,7 @@ public class FeignLoadBalancerTests { .thenReturn(true); when(this.config.get(ConnectTimeout)).thenReturn(this.defaultConnectTimeout); when(this.config.get(ReadTimeout)).thenReturn(this.defaultReadTimeout); + this.retryTemplate = new RetryTemplate(); } @Test @@ -75,7 +79,7 @@ public class FeignLoadBalancerTests { public void testUriInsecure() { when(this.config.get(IsSecure)).thenReturn(false); this.feignLoadBalancer = new FeignLoadBalancer(this.lb, this.config, - this.inspector); + this.inspector, retryTemplate, loadBalancedRetryPolicyFactory); Request request = new RequestTemplate().method("GET").append("http://foo/") .request(); RibbonRequest ribbonRequest = new RibbonRequest(this.delegate, request, @@ -96,7 +100,7 @@ public class FeignLoadBalancerTests { public void testSecureUriFromClientConfig() { when(this.config.get(IsSecure)).thenReturn(true); this.feignLoadBalancer = new FeignLoadBalancer(this.lb, this.config, - this.inspector); + this.inspector, retryTemplate, loadBalancedRetryPolicyFactory); Server server = new Server("foo", 7777); URI uri = this.feignLoadBalancer.reconstructURIWithServer(server, new URI("http://foo/")); @@ -118,7 +122,7 @@ public class FeignLoadBalancerTests { public Map getMetadata(Server server) { return null; } - }); + }, retryTemplate, loadBalancedRetryPolicyFactory); Server server = new Server("foo", 7777); URI uri = this.feignLoadBalancer.reconstructURIWithServer(server, new URI("http://foo/")); @@ -129,7 +133,7 @@ public class FeignLoadBalancerTests { @SneakyThrows public void testSecureUriFromClientConfigOverride() { this.feignLoadBalancer = new FeignLoadBalancer(this.lb, this.config, - this.inspector); + this.inspector, retryTemplate, loadBalancedRetryPolicyFactory); Server server = Mockito.mock(Server.class); when(server.getPort()).thenReturn(443); when(server.getHost()).thenReturn("foo"); diff --git a/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/feign/ribbon/FeignRibbonClientRetryTests.java b/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/feign/ribbon/FeignRibbonClientRetryTests.java index 5b066bc9..e8f0d794 100644 --- a/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/feign/ribbon/FeignRibbonClientRetryTests.java +++ b/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/feign/ribbon/FeignRibbonClientRetryTests.java @@ -16,14 +16,11 @@ package org.springframework.cloud.netflix.feign.ribbon; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; - -import java.lang.reflect.InvocationHandler; -import java.lang.reflect.Proxy; -import java.util.concurrent.atomic.AtomicInteger; - +import com.netflix.loadbalancer.Server; +import com.netflix.loadbalancer.ServerList; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; @@ -44,12 +41,13 @@ import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RestController; -import com.netflix.loadbalancer.Server; -import com.netflix.loadbalancer.ServerList; +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.Proxy; +import java.util.concurrent.atomic.AtomicInteger; -import lombok.AllArgsConstructor; -import lombok.Data; -import lombok.NoArgsConstructor; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; /** * Tests the Feign Retryer, not ribbon retry. @@ -58,7 +56,8 @@ import lombok.NoArgsConstructor; @RunWith(SpringJUnit4ClassRunner.class) @SpringBootTest(classes = FeignRibbonClientRetryTests.Application.class, webEnvironment = WebEnvironment.RANDOM_PORT, value = { "spring.application.name=feignclientretrytest", "feign.okhttp.enabled=false", - "feign.httpclient.enabled=false", "feign.hystrix.enabled=false", }) + "feign.httpclient.enabled=false", "feign.hystrix.enabled=false", "localapp.ribbon.MaxAutoRetries=2", + "localapp.ribbon.MaxAutoRetriesNextServer=3"}) @DirtiesContext public class FeignRibbonClientRetryTests { diff --git a/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/feign/ribbon/FeignRibbonClientTests.java b/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/feign/ribbon/FeignRibbonClientTests.java index 63e8753a..dbf3159f 100644 --- a/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/feign/ribbon/FeignRibbonClientTests.java +++ b/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/feign/ribbon/FeignRibbonClientTests.java @@ -16,19 +16,6 @@ package org.springframework.cloud.netflix.feign.ribbon; -import static org.mockito.Matchers.any; -import static org.mockito.Matchers.argThat; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -import org.hamcrest.CustomMatcher; -import org.junit.Before; -import org.junit.Test; -import org.springframework.cloud.netflix.ribbon.DefaultServerIntrospector; -import org.springframework.cloud.netflix.ribbon.ServerIntrospector; -import org.springframework.cloud.netflix.ribbon.SpringClientFactory; - import com.netflix.client.config.CommonClientConfigKey; import com.netflix.client.config.DefaultClientConfigImpl; import com.netflix.client.config.IClientConfig; @@ -37,11 +24,24 @@ import com.netflix.loadbalancer.ILoadBalancer; import com.netflix.loadbalancer.LoadBalancerStats; import com.netflix.loadbalancer.Server; import com.netflix.loadbalancer.ServerStats; - import feign.Client; import feign.Request; import feign.Request.Options; import feign.RequestTemplate; +import org.hamcrest.CustomMatcher; +import org.junit.Before; +import org.junit.Test; +import org.springframework.cloud.netflix.ribbon.DefaultServerIntrospector; +import org.springframework.cloud.netflix.ribbon.RibbonLoadBalancedRetryPolicyFactory; +import org.springframework.cloud.netflix.ribbon.ServerIntrospector; +import org.springframework.cloud.netflix.ribbon.SpringClientFactory; +import org.springframework.retry.support.RetryTemplate; + +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.argThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; /** * @author Dave Syer @@ -51,6 +51,7 @@ public class FeignRibbonClientTests { private AbstractLoadBalancer loadBalancer = mock(AbstractLoadBalancer.class); private Client delegate = mock(Client.class); + private RibbonLoadBalancedRetryPolicyFactory retryPolicyFactory = mock(RibbonLoadBalancedRetryPolicyFactory.class); private SpringClientFactory factory = new SpringClientFactory() { @Override @@ -79,7 +80,8 @@ public class FeignRibbonClientTests { // Even though we don't maintain FeignRibbonClient, keep these tests // around to make sure the expected behaviour doesn't break - private Client client = new LoadBalancerFeignClient(this.delegate, new CachingSpringLoadBalancerFactory(this.factory), this.factory); + private Client client = new LoadBalancerFeignClient(this.delegate, new CachingSpringLoadBalancerFactory(this.factory, + new RetryTemplate(), retryPolicyFactory), this.factory); @Before public void init() { diff --git a/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/feign/valid/FeignClientValidationTests.java b/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/feign/valid/FeignClientValidationTests.java index f255c3a8..26be6f78 100644 --- a/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/feign/valid/FeignClientValidationTests.java +++ b/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/feign/valid/FeignClientValidationTests.java @@ -17,6 +17,7 @@ package org.springframework.cloud.netflix.feign.valid; import org.junit.Test; +import org.springframework.cloud.client.loadbalancer.LoadBalancerAutoConfiguration; import org.springframework.cloud.netflix.feign.EnableFeignClients; import org.springframework.cloud.netflix.feign.FeignAutoConfiguration; import org.springframework.cloud.netflix.feign.FeignClient; @@ -82,6 +83,7 @@ public class FeignClientValidationTests { @Test public void validLoadBalanced() { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext( + LoadBalancerAutoConfiguration.class, RibbonAutoConfiguration.class, FeignRibbonClientAutoConfiguration.class, GoodServiceIdConfiguration.class);