diff --git a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/ribbon/RibbonClientConfiguration.java b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/ribbon/RibbonClientConfiguration.java index b574cd9e..0f955844 100644 --- a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/ribbon/RibbonClientConfiguration.java +++ b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/ribbon/RibbonClientConfiguration.java @@ -28,6 +28,8 @@ import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Lazy; import org.springframework.web.util.UriComponentsBuilder; +import com.netflix.client.DefaultLoadBalancerRetryHandler; +import com.netflix.client.RetryHandler; import com.netflix.client.config.DefaultClientConfigImpl; import com.netflix.client.config.IClientConfig; import com.netflix.loadbalancer.ConfigurationBasedServerList; @@ -98,15 +100,18 @@ public class RibbonClientConfiguration { * * @param config the configuration to use by the underlying Ribbon instance * @param loadBalancer the load balancer to use by the underlying Ribbon instance + * @param serverIntrospector server introspector to use by the underlying Ribbon instance + * @param retryHandler retry handler to use by the underlying Ribbon instance * @return a {@link RestClient} instances backed by Ribbon */ @Bean @Lazy @ConditionalOnMissingBean public RestClient ribbonRestClient(IClientConfig config, ILoadBalancer loadBalancer, - ServerIntrospector serverIntrospector) { + ServerIntrospector serverIntrospector, RetryHandler retryHandler) { RestClient client = new OverrideRestClient(config, serverIntrospector); client.setLoadBalancer(loadBalancer); + client.setRetryHandler(retryHandler); Monitors.registerObject("Client_" + this.name, client); return client; } @@ -134,10 +139,16 @@ public class RibbonClientConfiguration { @Bean @ConditionalOnMissingBean public RibbonLoadBalancerContext ribbonLoadBalancerContext( - ILoadBalancer loadBalancer, IClientConfig config) { - return new RibbonLoadBalancerContext(loadBalancer, config); + ILoadBalancer loadBalancer, IClientConfig config, RetryHandler retryHandler) { + return new RibbonLoadBalancerContext(loadBalancer, config, retryHandler); } + @Bean + @ConditionalOnMissingBean + public RetryHandler retryHandler(IClientConfig config) { + return new DefaultLoadBalancerRetryHandler(config); + } + @Bean @ConditionalOnMissingBean public ServerIntrospector serverIntrospector() { diff --git a/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/ribbon/RibbonClientPreprocessorOverridesRetryTests.java b/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/ribbon/RibbonClientPreprocessorOverridesRetryTests.java new file mode 100644 index 00000000..ecabe95d --- /dev/null +++ b/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/ribbon/RibbonClientPreprocessorOverridesRetryTests.java @@ -0,0 +1,118 @@ +/* + * 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.net.ConnectException; +import java.net.SocketException; +import java.net.SocketTimeoutException; +import java.net.UnknownHostException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.PropertyPlaceholderAutoConfiguration; +import org.springframework.boot.test.SpringApplicationConfiguration; +import org.springframework.cloud.netflix.archaius.ArchaiusAutoConfiguration; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Import; +import org.springframework.test.annotation.DirtiesContext; +import org.springframework.test.context.TestPropertySource; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import org.springframework.util.Assert; + +import com.netflix.client.DefaultLoadBalancerRetryHandler; +import com.netflix.client.RetryHandler; +import com.netflix.client.config.IClientConfig; + +/** + * @author Tyler Van Gorder + */ +@RunWith(SpringJUnit4ClassRunner.class) +@SpringApplicationConfiguration(classes = RibbonClientPreprocessorOverridesRetryTests.TestConfiguration.class) +@TestPropertySource(properties = { "customRetry.ribbon.MaxAutoRetries=0", + "customRetry.ribbon.MaxAutoRetriesNextServer=1", + "customRetry.ribbon.OkToRetryOnAllOperations=true" }) +@DirtiesContext +public class RibbonClientPreprocessorOverridesRetryTests { + + @Autowired + private SpringClientFactory factory; + + @Test + public void customRetryIsConfigured() throws Exception { + RibbonLoadBalancerContext context = (RibbonLoadBalancerContext) this.factory + .getLoadBalancerContext("customRetry"); + Assert.isInstanceOf(RetryRibbonConfiguration.CustomRetryHandler.class, + context.getRetryHandler()); + Assert.isTrue(context.getRetryHandler().getMaxRetriesOnSameServer() == 0); + Assert.isTrue(context.getRetryHandler().getMaxRetriesOnNextServer() == 1); + Assert.isTrue(context.getRetryHandler().isCircuitTrippingException( + new UnknownHostException("Unknown Host"))); + } + + @Configuration + @RibbonClient(name = "customRetry", configuration = RetryRibbonConfiguration.class) + @Import({ PropertyPlaceholderAutoConfiguration.class, + ArchaiusAutoConfiguration.class, RibbonAutoConfiguration.class }) + protected static class TestConfiguration { + } + +} + +@Configuration +class RetryRibbonConfiguration { + @Bean + public RetryHandler retryHandler(IClientConfig config) { + return new CustomRetryHandler(config); + } + + class CustomRetryHandler extends DefaultLoadBalancerRetryHandler { + + @SuppressWarnings("unchecked") + private List> retriable = new ArrayList(){{ + add(UnknownHostException.class); + add(ConnectException.class); + add(SocketTimeoutException.class); + }}; + + @SuppressWarnings("unchecked") + private List> circuitRelated = new ArrayList(){{ + add(UnknownHostException.class); + add(SocketException.class); + add(SocketTimeoutException.class); + }}; + + CustomRetryHandler(IClientConfig config) { + super(config); + } + + @Override + protected List> getRetriableExceptions() { + return retriable; + } + + @Override + protected List> getCircuitRelatedExceptions() { + return circuitRelated; + } + + } +}