From 576baf6fffa5a693abaca3ba712e41f654d6b7d6 Mon Sep 17 00:00:00 2001 From: saga Date: Thu, 25 Jan 2018 20:45:35 +0800 Subject: [PATCH] Improve Zuul Hystrix Timeouts (#2645) --- .../main/asciidoc/spring-cloud-netflix.adoc | 41 +++- .../route/support/AbstractRibbonCommand.java | 47 ++++- .../HttpClientRibbonCommandFactoryTest.java | 197 +++++++++++++++++- .../OkHttpRibbonCommandFactoryTest.java | 189 ++++++++++++++++- 4 files changed, 451 insertions(+), 23 deletions(-) diff --git a/docs/src/main/asciidoc/spring-cloud-netflix.adoc b/docs/src/main/asciidoc/spring-cloud-netflix.adoc index 00e6ebc9a..41a4af22d 100644 --- a/docs/src/main/asciidoc/spring-cloud-netflix.adoc +++ b/docs/src/main/asciidoc/spring-cloud-netflix.adoc @@ -2115,11 +2115,44 @@ class MyFallbackProvider implements FallbackProvider { === Zuul Timeouts -If you want to configure the socket timeouts and read timeouts for requests proxied through -Zuul there are two options based on your configuration. +==== Service Discovery Configuration -If Zuul is using service discovery than you need to configure these timeouts via Ribbon properties, -`ribbon.ReadTimeout` and `ribbon.SocketTimeout`. +If Zuul is using service discovery there are two timeouts you need to be concerned +with, the Hystrix timeout (since all routes are wrapped in Hystrix commands by default) +and the Ribbon timeout. The Hystrix timeout needs to take into account the Ribbon +read and connect timeout PLUS the total number of retries that will happen for that +service. By default Spring Cloud Zuul will do its best to calculate the Hystrix timeout +for you *UNLESS* you specify the Hystrix timeout explicitly. + +The Hystrix timeout is calculated using the following formula: +``` +(ribbon.ConnectTimeout + ribbon.ReadTimeout) * (ribbon.MaxAutoRetries + 1) * (ribbon.MaxAutoRetriesNextServer + 1) +``` + +As an example, if you set the following properties in your application properties + +.application.yml +---- +ribbon: + ReadTimeout:100 + ConnectTimeout:500 + MaxAutoRetries:1 + MaxAutoRetriesNextServer:1 +---- +Then the Hystrix timeout (for all routes in this case) will be set to `2400ms.` + +NOTE: You can configure the Hystrix timeout for individual routes using `service.ribbon.*` properties. + +NOTE: If you choose to not configure the above properties than the default values will be used therefore the +default Hystrix timeout will be set to `4000ms`. + +If you set `hystrix.command.commandKey.execution.isolation.thread.timeoutInMilliseconds`, where +`commandKey` is the route id, or set `hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds` +than these values will be used for the Hystrix timeout regardless of what you have set for the `ribbon.*` properties. +If you set either of these properties **YOU** are responsible for making sure it takes +into account the Ribbon connect and read timeouts as well as any retries that may happen. + +==== URL Configuration If you have configured Zuul routes by specifying URLs than you will need to use `zuul.host.connect-timeout-millis` and `zuul.host.socket-timeout-millis`. diff --git a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/zuul/filters/route/support/AbstractRibbonCommand.java b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/zuul/filters/route/support/AbstractRibbonCommand.java index a21b636aa..2461fd775 100644 --- a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/zuul/filters/route/support/AbstractRibbonCommand.java +++ b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/zuul/filters/route/support/AbstractRibbonCommand.java @@ -29,6 +29,7 @@ import org.springframework.cloud.netflix.zuul.filters.route.FallbackProvider; import org.springframework.http.client.ClientHttpResponse; import com.netflix.client.AbstractLoadBalancerAwareClient; import com.netflix.client.ClientRequest; +import com.netflix.client.config.DefaultClientConfigImpl; import com.netflix.client.config.IClientConfig; import com.netflix.client.config.IClientConfigKey; import com.netflix.client.http.HttpResponse; @@ -88,31 +89,55 @@ public abstract class AbstractRibbonCommand 0) { hystrixTimeout = commandHystrixTimeout; } - else if( defaultHystrixTimeout > 0) { + else if(defaultHystrixTimeout > 0) { hystrixTimeout = defaultHystrixTimeout; } else { hystrixTimeout = ribbonTimeout; } if(hystrixTimeout < ribbonTimeout) { LOGGER.warn("The Hystrix timeout of " + hystrixTimeout + "ms for the command " + commandKey + - " is set lower than the combination of the Ribbon read and connect timeout, " + ribbonTimeout + "ms."); + " is set lower than the combination of the Ribbon read and connect timeout, " + ribbonTimeout + "ms."); } - return HystrixCommandProperties.Setter().withExecutionIsolationStrategy( - zuulProperties.getRibbonIsolationStrategy()).withExecutionTimeoutInMilliseconds(hystrixTimeout); + return hystrixTimeout; + } + + protected static int getRibbonTimeout(IClientConfig config, String commandKey) { + int ribbonTimeout; + if (config == null) { + ribbonTimeout = RibbonClientConfiguration.DEFAULT_READ_TIMEOUT + RibbonClientConfiguration.DEFAULT_CONNECT_TIMEOUT; + } else { + int ribbonReadTimeout = getTimeout(config, commandKey, "ReadTimeout", + IClientConfigKey.Keys.ReadTimeout, RibbonClientConfiguration.DEFAULT_READ_TIMEOUT); + int ribbonConnectTimeout = getTimeout(config, commandKey, "ConnectTimeout", + IClientConfigKey.Keys.ConnectTimeout, RibbonClientConfiguration.DEFAULT_CONNECT_TIMEOUT); + int maxAutoRetries = getTimeout(config, commandKey, "MaxAutoRetries", + IClientConfigKey.Keys.MaxAutoRetries, DefaultClientConfigImpl.DEFAULT_MAX_AUTO_RETRIES); + int maxAutoRetriesNextServer = getTimeout(config, commandKey, "MaxAutoRetriesNextServer", + IClientConfigKey.Keys.MaxAutoRetriesNextServer, DefaultClientConfigImpl.DEFAULT_MAX_AUTO_RETRIES_NEXT_SERVER); + ribbonTimeout = (ribbonReadTimeout + ribbonConnectTimeout) * (maxAutoRetries + 1) * (maxAutoRetriesNextServer + 1); + } + return ribbonTimeout; + } + + private static int getTimeout(IClientConfig config, String commandKey, String property, IClientConfigKey configKey, int defaultValue) { + DynamicPropertyFactory dynamicPropertyFactory = DynamicPropertyFactory.getInstance(); + return dynamicPropertyFactory.getIntProperty(commandKey + "." + config.getNameSpace() + "." + property, config.get(configKey, defaultValue)).get(); } @Deprecated diff --git a/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/zuul/filters/route/apache/HttpClientRibbonCommandFactoryTest.java b/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/zuul/filters/route/apache/HttpClientRibbonCommandFactoryTest.java index 5ef1fdf77..50da3bb75 100644 --- a/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/zuul/filters/route/apache/HttpClientRibbonCommandFactoryTest.java +++ b/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/zuul/filters/route/apache/HttpClientRibbonCommandFactoryTest.java @@ -24,6 +24,7 @@ import static org.mockito.Mockito.mock; /** * @author Ryan Baxter + * @author Gang Li */ public class HttpClientRibbonCommandFactoryTest { @@ -32,13 +33,13 @@ public class HttpClientRibbonCommandFactoryTest { HttpClientRibbonCommandFactory ribbonCommandFactory; @Before - public void setup(){ + public void setup() { this.springClientFactory = mock(SpringClientFactory.class); this.zuulProperties = new ZuulProperties(); RibbonLoadBalancingHttpClient loadBalancingHttpClient = mock(RibbonLoadBalancingHttpClient.class); IClientConfig clientConfig = new DefaultClientConfigImpl(); doReturn(loadBalancingHttpClient).when(this.springClientFactory).getClient(anyString(), - eq(RibbonLoadBalancingHttpClient.class)); + eq(RibbonLoadBalancingHttpClient.class)); doReturn(clientConfig).when(this.springClientFactory).getClientConfig(anyString()); this.ribbonCommandFactory = new HttpClientRibbonCommandFactory(springClientFactory, zuulProperties, new HashSet()); } @@ -54,7 +55,7 @@ public class HttpClientRibbonCommandFactoryTest { RibbonCommandContext context = mock(RibbonCommandContext.class); doReturn("service").when(context).getServiceId(); HttpClientRibbonCommand ribbonCommand = this.ribbonCommandFactory.create(context); - assertEquals(2000, ribbonCommand.getProperties().executionTimeoutInMilliseconds().get().intValue()); + assertEquals(4000, ribbonCommand.getProperties().executionTimeoutInMilliseconds().get().intValue()); } @Test @@ -94,13 +95,199 @@ public class HttpClientRibbonCommandFactoryTest { clientConfig.set(IClientConfigKey.Keys.ConnectTimeout, 100); clientConfig.set(IClientConfigKey.Keys.ReadTimeout, 500); doReturn(loadBalancingHttpClient).when(springClientFactory).getClient(anyString(), - eq(RibbonLoadBalancingHttpClient.class)); + eq(RibbonLoadBalancingHttpClient.class)); doReturn(clientConfig).when(springClientFactory).getClientConfig(anyString()); HttpClientRibbonCommandFactory ribbonCommandFactory = new HttpClientRibbonCommandFactory(springClientFactory, zuulProperties, new HashSet()); RibbonCommandContext context = mock(RibbonCommandContext.class); doReturn("service").when(context).getServiceId(); HttpClientRibbonCommand ribbonCommand = ribbonCommandFactory.create(context); - assertEquals(600, ribbonCommand.getProperties().executionTimeoutInMilliseconds().get().intValue()); + assertEquals(1200, ribbonCommand.getProperties().executionTimeoutInMilliseconds().get().intValue()); + } + + @Test + public void testHystrixDefaultAndRibbonSetting() throws Exception { + ConfigurationManager.getConfigInstance().setProperty("hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds", 30); + ConfigurationManager.getConfigInstance().setProperty("service.ribbon.ConnectTimeout", 1000); + ConfigurationManager.getConfigInstance().setProperty("service.ribbon.ReadTimeout", 1000); + ConfigurationManager.getConfigInstance().setProperty("service.ribbon.MaxAutoRetries", 1); + ConfigurationManager.getConfigInstance().setProperty("service.ribbon.MaxAutoRetriesNextServer", 2); + SpringClientFactory springClientFactory = mock(SpringClientFactory.class); + ZuulProperties zuulProperties = new ZuulProperties(); + RibbonLoadBalancingHttpClient loadBalancingHttpClient = mock(RibbonLoadBalancingHttpClient.class); + IClientConfig clientConfig = new DefaultClientConfigImpl(); + clientConfig.set(IClientConfigKey.Keys.ConnectTimeout, 100); + clientConfig.set(IClientConfigKey.Keys.ReadTimeout, 500); + doReturn(loadBalancingHttpClient).when(springClientFactory).getClient(anyString(), + eq(RibbonLoadBalancingHttpClient.class)); + doReturn(clientConfig).when(springClientFactory).getClientConfig(anyString()); + HttpClientRibbonCommandFactory ribbonCommandFactory = new HttpClientRibbonCommandFactory(springClientFactory, zuulProperties, new HashSet()); + RibbonCommandContext context = mock(RibbonCommandContext.class); + doReturn("service").when(context).getServiceId(); + HttpClientRibbonCommand ribbonCommand = ribbonCommandFactory.create(context); + assertEquals(30, ribbonCommand.getProperties().executionTimeoutInMilliseconds().get().intValue()); + } + + @Test + public void testHystrixCommandAndRibbonSetting() throws Exception { + ConfigurationManager.getConfigInstance().setProperty("hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds", 30); + ConfigurationManager.getConfigInstance().setProperty("hystrix.command.service.execution.isolation.thread.timeoutInMilliseconds", 50); + ConfigurationManager.getConfigInstance().setProperty("service.ribbon.ConnectTimeout", 1000); + ConfigurationManager.getConfigInstance().setProperty("service.ribbon.ReadTimeout", 1000); + ConfigurationManager.getConfigInstance().setProperty("service.ribbon.MaxAutoRetries", 1); + ConfigurationManager.getConfigInstance().setProperty("service.ribbon.MaxAutoRetriesNextServer", 2); + SpringClientFactory springClientFactory = mock(SpringClientFactory.class); + ZuulProperties zuulProperties = new ZuulProperties(); + RibbonLoadBalancingHttpClient loadBalancingHttpClient = mock(RibbonLoadBalancingHttpClient.class); + IClientConfig clientConfig = new DefaultClientConfigImpl(); + clientConfig.set(IClientConfigKey.Keys.ConnectTimeout, 100); + clientConfig.set(IClientConfigKey.Keys.ReadTimeout, 500); + doReturn(loadBalancingHttpClient).when(springClientFactory).getClient(anyString(), + eq(RibbonLoadBalancingHttpClient.class)); + doReturn(clientConfig).when(springClientFactory).getClientConfig(anyString()); + HttpClientRibbonCommandFactory ribbonCommandFactory = new HttpClientRibbonCommandFactory(springClientFactory, zuulProperties, new HashSet()); + RibbonCommandContext context = mock(RibbonCommandContext.class); + doReturn("service").when(context).getServiceId(); + HttpClientRibbonCommand ribbonCommand = ribbonCommandFactory.create(context); + assertEquals(50, ribbonCommand.getProperties().executionTimeoutInMilliseconds().get().intValue()); + } + + @Test + public void testDefaultRibbonSetting() throws Exception { + SpringClientFactory springClientFactory = mock(SpringClientFactory.class); + ZuulProperties zuulProperties = new ZuulProperties(); + RibbonLoadBalancingHttpClient loadBalancingHttpClient = mock(RibbonLoadBalancingHttpClient.class); + IClientConfig clientConfig = new DefaultClientConfigImpl(); + doReturn(loadBalancingHttpClient).when(springClientFactory).getClient(anyString(), + eq(RibbonLoadBalancingHttpClient.class)); + doReturn(clientConfig).when(springClientFactory).getClientConfig(anyString()); + HttpClientRibbonCommandFactory commandFactory = new HttpClientRibbonCommandFactory(springClientFactory, zuulProperties, new HashSet()); + RibbonCommandContext context = mock(RibbonCommandContext.class); + doReturn("service").when(context).getServiceId(); + HttpClientRibbonCommand ribbonCommand = commandFactory.create(context); + assertEquals(4000, ribbonCommand.getProperties().executionTimeoutInMilliseconds().get().intValue()); + } + + @Test + public void testRibbonTimeoutAndRibbonRetriesDefaultAndNameSpaceSetting() throws Exception { + ConfigurationManager.getConfigInstance().setProperty("service.test.ConnectTimeout", 1000); + ConfigurationManager.getConfigInstance().setProperty("service.test.ReadTimeout", 1000); + SpringClientFactory springClientFactory = mock(SpringClientFactory.class); + ZuulProperties zuulProperties = new ZuulProperties(); + RibbonLoadBalancingHttpClient loadBalancingHttpClient = mock(RibbonLoadBalancingHttpClient.class); + IClientConfig clientConfig = new DefaultClientConfigImpl(); + clientConfig.set(IClientConfigKey.Keys.ConnectTimeout, 100); + clientConfig.set(IClientConfigKey.Keys.ReadTimeout, 500); + doReturn(loadBalancingHttpClient).when(springClientFactory).getClient(anyString(), + eq(RibbonLoadBalancingHttpClient.class)); + doReturn(clientConfig).when(springClientFactory).getClientConfig(anyString()); + HttpClientRibbonCommandFactory ribbonCommandFactory = new HttpClientRibbonCommandFactory(springClientFactory, zuulProperties, new HashSet()); + RibbonCommandContext context = mock(RibbonCommandContext.class); + doReturn("service").when(context).getServiceId(); + HttpClientRibbonCommand ribbonCommand = ribbonCommandFactory.create(context); + assertEquals(1200, ribbonCommand.getProperties().executionTimeoutInMilliseconds().get().intValue()); + } + + @Test + public void testRibbonTimeoutAndRibbonRetriesDefaultAndDefaultSpaceSetting() throws Exception { + ConfigurationManager.getConfigInstance().setProperty("service.ribbon.ConnectTimeout", 1000); + ConfigurationManager.getConfigInstance().setProperty("service.ribbon.ReadTimeout", 1000); + SpringClientFactory springClientFactory = mock(SpringClientFactory.class); + ZuulProperties zuulProperties = new ZuulProperties(); + RibbonLoadBalancingHttpClient loadBalancingHttpClient = mock(RibbonLoadBalancingHttpClient.class); + IClientConfig clientConfig = new DefaultClientConfigImpl(); + clientConfig.set(IClientConfigKey.Keys.ConnectTimeout, 100); + clientConfig.set(IClientConfigKey.Keys.ReadTimeout, 500); + doReturn(loadBalancingHttpClient).when(springClientFactory).getClient(anyString(), + eq(RibbonLoadBalancingHttpClient.class)); + doReturn(clientConfig).when(springClientFactory).getClientConfig(anyString()); + HttpClientRibbonCommandFactory ribbonCommandFactory = new HttpClientRibbonCommandFactory(springClientFactory, zuulProperties, new HashSet()); + RibbonCommandContext context = mock(RibbonCommandContext.class); + doReturn("service").when(context).getServiceId(); + HttpClientRibbonCommand ribbonCommand = ribbonCommandFactory.create(context); + assertEquals(4000, ribbonCommand.getProperties().executionTimeoutInMilliseconds().get().intValue()); + } + + @Test + public void testRibbonTimeoutAndRibbonNameSpaceRetriesDefaultAndDefaultSpaceSetting() throws Exception { + ConfigurationManager.getConfigInstance().setProperty("service.ribbon.ConnectTimeout", 1000); + ConfigurationManager.getConfigInstance().setProperty("service.ribbon.ReadTimeout", 1000); + ConfigurationManager.getConfigInstance().setProperty("service.test.MaxAutoRetriesNextServer", 2); + SpringClientFactory springClientFactory = mock(SpringClientFactory.class); + ZuulProperties zuulProperties = new ZuulProperties(); + RibbonLoadBalancingHttpClient loadBalancingHttpClient = mock(RibbonLoadBalancingHttpClient.class); + IClientConfig clientConfig = new DefaultClientConfigImpl(); + clientConfig.set(IClientConfigKey.Keys.ConnectTimeout, 100); + clientConfig.set(IClientConfigKey.Keys.ReadTimeout, 500); + doReturn(loadBalancingHttpClient).when(springClientFactory).getClient(anyString(), + eq(RibbonLoadBalancingHttpClient.class)); + doReturn(clientConfig).when(springClientFactory).getClientConfig(anyString()); + HttpClientRibbonCommandFactory ribbonCommandFactory = new HttpClientRibbonCommandFactory(springClientFactory, zuulProperties, new HashSet()); + RibbonCommandContext context = mock(RibbonCommandContext.class); + doReturn("service").when(context).getServiceId(); + HttpClientRibbonCommand ribbonCommand = ribbonCommandFactory.create(context); + assertEquals(4000, ribbonCommand.getProperties().executionTimeoutInMilliseconds().get().intValue()); + } + + @Test + public void testRibbonRetriesAndRibbonTimeoutSetting() throws Exception { + ConfigurationManager.getConfigInstance().setProperty("service.ribbon.MaxAutoRetries", 1); + ConfigurationManager.getConfigInstance().setProperty("service.ribbon.MaxAutoRetriesNextServer", 2); + SpringClientFactory springClientFactory = mock(SpringClientFactory.class); + ZuulProperties zuulProperties = new ZuulProperties(); + RibbonLoadBalancingHttpClient loadBalancingHttpClient = mock(RibbonLoadBalancingHttpClient.class); + IClientConfig clientConfig = new DefaultClientConfigImpl(); + clientConfig.set(IClientConfigKey.Keys.ConnectTimeout, 100); + clientConfig.set(IClientConfigKey.Keys.ReadTimeout, 500); + doReturn(loadBalancingHttpClient).when(springClientFactory).getClient(anyString(), + eq(RibbonLoadBalancingHttpClient.class)); + doReturn(clientConfig).when(springClientFactory).getClientConfig(anyString()); + HttpClientRibbonCommandFactory ribbonCommandFactory = new HttpClientRibbonCommandFactory(springClientFactory, zuulProperties, new HashSet()); + RibbonCommandContext context = mock(RibbonCommandContext.class); + doReturn("service").when(context).getServiceId(); + HttpClientRibbonCommand ribbonCommand = ribbonCommandFactory.create(context); + assertEquals(3600, ribbonCommand.getProperties().executionTimeoutInMilliseconds().get().intValue()); + } + + @Test + public void testRibbonCommandRetriesAndRibbonCommandTimeoutSetting() throws Exception { + ConfigurationManager.getConfigInstance().setProperty("service.ribbon.ConnectTimeout", 1000); + ConfigurationManager.getConfigInstance().setProperty("service.ribbon.ReadTimeout", 1000); + ConfigurationManager.getConfigInstance().setProperty("service.ribbon.MaxAutoRetries", 1); + ConfigurationManager.getConfigInstance().setProperty("service.ribbon.MaxAutoRetriesNextServer", 2); + SpringClientFactory springClientFactory = mock(SpringClientFactory.class); + ZuulProperties zuulProperties = new ZuulProperties(); + RibbonLoadBalancingHttpClient loadBalancingHttpClient = mock(RibbonLoadBalancingHttpClient.class); + IClientConfig clientConfig = new DefaultClientConfigImpl(); + clientConfig.set(IClientConfigKey.Keys.ConnectTimeout, 100); + clientConfig.set(IClientConfigKey.Keys.ReadTimeout, 500); + doReturn(loadBalancingHttpClient).when(springClientFactory).getClient(anyString(), + eq(RibbonLoadBalancingHttpClient.class)); + doReturn(clientConfig).when(springClientFactory).getClientConfig(anyString()); + HttpClientRibbonCommandFactory ribbonCommandFactory = new HttpClientRibbonCommandFactory(springClientFactory, zuulProperties, new HashSet()); + RibbonCommandContext context = mock(RibbonCommandContext.class); + doReturn("service").when(context).getServiceId(); + HttpClientRibbonCommand ribbonCommand = ribbonCommandFactory.create(context); + assertEquals(12000, ribbonCommand.getProperties().executionTimeoutInMilliseconds().get().intValue()); + } + + @Test + public void testRibbonCommandRetriesAndRibbonCommandTimeoutPartOfSetting() throws Exception { + ConfigurationManager.getConfigInstance().setProperty("service.ribbon.ConnectTimeout", 1000); + ConfigurationManager.getConfigInstance().setProperty("service.ribbon.MaxAutoRetries", 1); + SpringClientFactory springClientFactory = mock(SpringClientFactory.class); + ZuulProperties zuulProperties = new ZuulProperties(); + RibbonLoadBalancingHttpClient loadBalancingHttpClient = mock(RibbonLoadBalancingHttpClient.class); + IClientConfig clientConfig = new DefaultClientConfigImpl(); + clientConfig.set(IClientConfigKey.Keys.ConnectTimeout, 100); + clientConfig.set(IClientConfigKey.Keys.ReadTimeout, 500); + doReturn(loadBalancingHttpClient).when(springClientFactory).getClient(anyString(), + eq(RibbonLoadBalancingHttpClient.class)); + doReturn(clientConfig).when(springClientFactory).getClientConfig(anyString()); + HttpClientRibbonCommandFactory ribbonCommandFactory = new HttpClientRibbonCommandFactory(springClientFactory, zuulProperties, new HashSet()); + RibbonCommandContext context = mock(RibbonCommandContext.class); + doReturn("service").when(context).getServiceId(); + HttpClientRibbonCommand ribbonCommand = ribbonCommandFactory.create(context); + assertEquals(6000, ribbonCommand.getProperties().executionTimeoutInMilliseconds().get().intValue()); } } \ No newline at end of file diff --git a/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/zuul/filters/route/okhttp/OkHttpRibbonCommandFactoryTest.java b/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/zuul/filters/route/okhttp/OkHttpRibbonCommandFactoryTest.java index 4fcec5619..0590bfee6 100644 --- a/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/zuul/filters/route/okhttp/OkHttpRibbonCommandFactoryTest.java +++ b/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/zuul/filters/route/okhttp/OkHttpRibbonCommandFactoryTest.java @@ -24,6 +24,7 @@ import static org.mockito.Mockito.mock; /** * @author Ryan Baxter + * @author Gang Li */ public class OkHttpRibbonCommandFactoryTest { @@ -54,7 +55,7 @@ public class OkHttpRibbonCommandFactoryTest { RibbonCommandContext context = mock(RibbonCommandContext.class); doReturn("service").when(context).getServiceId(); OkHttpRibbonCommand ribbonCommand = this.commandFactory.create(context); - assertEquals(2000, ribbonCommand.getProperties().executionTimeoutInMilliseconds().get().intValue()); + assertEquals(4000, ribbonCommand.getProperties().executionTimeoutInMilliseconds().get().intValue()); } @Test @@ -86,7 +87,66 @@ public class OkHttpRibbonCommandFactoryTest { } @Test - public void testHystrixTimeoutValueRibbonTimeouts() throws Exception { + public void testHystrixDefaultAndRibbonSetting() throws Exception { + ConfigurationManager.getConfigInstance().setProperty("hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds", 30); + ConfigurationManager.getConfigInstance().setProperty("service.ribbon.ConnectTimeout", 1000); + ConfigurationManager.getConfigInstance().setProperty("service.ribbon.ReadTimeout", 1000); + ConfigurationManager.getConfigInstance().setProperty("service.ribbon.MaxAutoRetries", 1); + ConfigurationManager.getConfigInstance().setProperty("service.ribbon.MaxAutoRetriesNextServer", 2); + SpringClientFactory springClientFactory = mock(SpringClientFactory.class); + ZuulProperties zuulProperties = new ZuulProperties(); + OkHttpLoadBalancingClient loadBalancingHttpClient = mock(OkHttpLoadBalancingClient.class); + IClientConfig clientConfig = new DefaultClientConfigImpl(); + doReturn(loadBalancingHttpClient).when(springClientFactory).getClient(anyString(), + eq(OkHttpLoadBalancingClient.class)); + doReturn(clientConfig).when(springClientFactory).getClientConfig(anyString()); + OkHttpRibbonCommandFactory commandFactory = new OkHttpRibbonCommandFactory(springClientFactory, zuulProperties, new HashSet()); + RibbonCommandContext context = mock(RibbonCommandContext.class); + doReturn("service").when(context).getServiceId(); + OkHttpRibbonCommand ribbonCommand = commandFactory.create(context); + assertEquals(30, ribbonCommand.getProperties().executionTimeoutInMilliseconds().get().intValue()); + } + + @Test + public void testHystrixCommandAndRibbonSetting() throws Exception { + ConfigurationManager.getConfigInstance().setProperty("hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds", 30); + ConfigurationManager.getConfigInstance().setProperty("hystrix.command.service.execution.isolation.thread.timeoutInMilliseconds", 50); + ConfigurationManager.getConfigInstance().setProperty("service.ribbon.ConnectTimeout", 1000); + ConfigurationManager.getConfigInstance().setProperty("service.ribbon.ReadTimeout", 1000); + ConfigurationManager.getConfigInstance().setProperty("service.ribbon.MaxAutoRetries", 1); + ConfigurationManager.getConfigInstance().setProperty("service.ribbon.MaxAutoRetriesNextServer", 2); + SpringClientFactory springClientFactory = mock(SpringClientFactory.class); + ZuulProperties zuulProperties = new ZuulProperties(); + OkHttpLoadBalancingClient loadBalancingHttpClient = mock(OkHttpLoadBalancingClient.class); + IClientConfig clientConfig = new DefaultClientConfigImpl(); + doReturn(loadBalancingHttpClient).when(springClientFactory).getClient(anyString(), + eq(OkHttpLoadBalancingClient.class)); + doReturn(clientConfig).when(springClientFactory).getClientConfig(anyString()); + OkHttpRibbonCommandFactory commandFactory = new OkHttpRibbonCommandFactory(springClientFactory, zuulProperties, new HashSet()); + RibbonCommandContext context = mock(RibbonCommandContext.class); + doReturn("service").when(context).getServiceId(); + OkHttpRibbonCommand ribbonCommand = commandFactory.create(context); + assertEquals(50, ribbonCommand.getProperties().executionTimeoutInMilliseconds().get().intValue()); + } + + @Test + public void testDefaultRibbonSetting() throws Exception { + SpringClientFactory springClientFactory = mock(SpringClientFactory.class); + ZuulProperties zuulProperties = new ZuulProperties(); + OkHttpLoadBalancingClient loadBalancingHttpClient = mock(OkHttpLoadBalancingClient.class); + IClientConfig clientConfig = new DefaultClientConfigImpl(); + doReturn(loadBalancingHttpClient).when(springClientFactory).getClient(anyString(), + eq(OkHttpLoadBalancingClient.class)); + doReturn(clientConfig).when(springClientFactory).getClientConfig(anyString()); + OkHttpRibbonCommandFactory commandFactory = new OkHttpRibbonCommandFactory(springClientFactory, zuulProperties, new HashSet()); + RibbonCommandContext context = mock(RibbonCommandContext.class); + doReturn("service").when(context).getServiceId(); + OkHttpRibbonCommand ribbonCommand = commandFactory.create(context); + assertEquals(4000, ribbonCommand.getProperties().executionTimeoutInMilliseconds().get().intValue()); + } + + @Test + public void testRibbonAndRibbonRetriesDefaultSetting() throws Exception { SpringClientFactory springClientFactory = mock(SpringClientFactory.class); ZuulProperties zuulProperties = new ZuulProperties(); OkHttpLoadBalancingClient loadBalancingHttpClient = mock(OkHttpLoadBalancingClient.class); @@ -100,7 +160,130 @@ public class OkHttpRibbonCommandFactoryTest { RibbonCommandContext context = mock(RibbonCommandContext.class); doReturn("service").when(context).getServiceId(); OkHttpRibbonCommand ribbonCommand = commandFactory.create(context); - assertEquals(600, ribbonCommand.getProperties().executionTimeoutInMilliseconds().get().intValue()); + assertEquals(1200, ribbonCommand.getProperties().executionTimeoutInMilliseconds().get().intValue()); + } + + @Test + public void testRibbonTimeoutAndRibbonRetriesDefaultAndNameSpaceSetting() throws Exception { + ConfigurationManager.getConfigInstance().setProperty("service.test.ConnectTimeout", 1000); + ConfigurationManager.getConfigInstance().setProperty("service.test.ReadTimeout", 1000); + SpringClientFactory springClientFactory = mock(SpringClientFactory.class); + ZuulProperties zuulProperties = new ZuulProperties(); + OkHttpLoadBalancingClient loadBalancingHttpClient = mock(OkHttpLoadBalancingClient.class); + IClientConfig clientConfig = new DefaultClientConfigImpl("test"); + clientConfig.set(IClientConfigKey.Keys.ConnectTimeout, 100); + clientConfig.set(IClientConfigKey.Keys.ReadTimeout, 500); + doReturn(loadBalancingHttpClient).when(springClientFactory).getClient(anyString(), + eq(OkHttpLoadBalancingClient.class)); + doReturn(clientConfig).when(springClientFactory).getClientConfig(anyString()); + OkHttpRibbonCommandFactory commandFactory = new OkHttpRibbonCommandFactory(springClientFactory, zuulProperties, new HashSet()); + RibbonCommandContext context = mock(RibbonCommandContext.class); + doReturn("service").when(context).getServiceId(); + OkHttpRibbonCommand ribbonCommand = commandFactory.create(context); + assertEquals(4000, ribbonCommand.getProperties().executionTimeoutInMilliseconds().get().intValue()); + } + + @Test + public void testRibbonTimeoutAndRibbonRetriesDefaultAndDefaultSpaceSetting() throws Exception { + ConfigurationManager.getConfigInstance().setProperty("service.ribbon.ConnectTimeout", 1000); + ConfigurationManager.getConfigInstance().setProperty("service.ribbon.ReadTimeout", 1000); + SpringClientFactory springClientFactory = mock(SpringClientFactory.class); + ZuulProperties zuulProperties = new ZuulProperties(); + OkHttpLoadBalancingClient loadBalancingHttpClient = mock(OkHttpLoadBalancingClient.class); + IClientConfig clientConfig = new DefaultClientConfigImpl(); + clientConfig.set(IClientConfigKey.Keys.ConnectTimeout, 100); + clientConfig.set(IClientConfigKey.Keys.ReadTimeout, 500); + doReturn(loadBalancingHttpClient).when(springClientFactory).getClient(anyString(), + eq(OkHttpLoadBalancingClient.class)); + doReturn(clientConfig).when(springClientFactory).getClientConfig(anyString()); + OkHttpRibbonCommandFactory commandFactory = new OkHttpRibbonCommandFactory(springClientFactory, zuulProperties, new HashSet()); + RibbonCommandContext context = mock(RibbonCommandContext.class); + doReturn("service").when(context).getServiceId(); + OkHttpRibbonCommand ribbonCommand = commandFactory.create(context); + assertEquals(4000, ribbonCommand.getProperties().executionTimeoutInMilliseconds().get().intValue()); + } + + @Test + public void testRibbonTimeoutAndRibbonNameSpaceRetriesDefaultAndDefaultSpaceSetting() throws Exception { + ConfigurationManager.getConfigInstance().setProperty("service.ribbon.ConnectTimeout", 1000); + ConfigurationManager.getConfigInstance().setProperty("service.ribbon.ReadTimeout", 1000); + ConfigurationManager.getConfigInstance().setProperty("service.test.MaxAutoRetriesNextServer", 2); + SpringClientFactory springClientFactory = mock(SpringClientFactory.class); + ZuulProperties zuulProperties = new ZuulProperties(); + OkHttpLoadBalancingClient loadBalancingHttpClient = mock(OkHttpLoadBalancingClient.class); + IClientConfig clientConfig = new DefaultClientConfigImpl("test"); + clientConfig.set(IClientConfigKey.Keys.ConnectTimeout, 100); + clientConfig.set(IClientConfigKey.Keys.ReadTimeout, 500); + doReturn(loadBalancingHttpClient).when(springClientFactory).getClient(anyString(), + eq(OkHttpLoadBalancingClient.class)); + doReturn(clientConfig).when(springClientFactory).getClientConfig(anyString()); + OkHttpRibbonCommandFactory commandFactory = new OkHttpRibbonCommandFactory(springClientFactory, zuulProperties, new HashSet()); + RibbonCommandContext context = mock(RibbonCommandContext.class); + doReturn("service").when(context).getServiceId(); + OkHttpRibbonCommand ribbonCommand = commandFactory.create(context); + assertEquals(1800, ribbonCommand.getProperties().executionTimeoutInMilliseconds().get().intValue()); + } + + @Test + public void testRibbonRetriesAndRibbonTimeoutSetting() throws Exception { + ConfigurationManager.getConfigInstance().setProperty("service.ribbon.MaxAutoRetries", 1); + ConfigurationManager.getConfigInstance().setProperty("service.ribbon.MaxAutoRetriesNextServer", 2); + SpringClientFactory springClientFactory = mock(SpringClientFactory.class); + ZuulProperties zuulProperties = new ZuulProperties(); + OkHttpLoadBalancingClient loadBalancingHttpClient = mock(OkHttpLoadBalancingClient.class); + IClientConfig clientConfig = new DefaultClientConfigImpl(); + clientConfig.set(IClientConfigKey.Keys.ConnectTimeout, 100); + clientConfig.set(IClientConfigKey.Keys.ReadTimeout, 500); + doReturn(loadBalancingHttpClient).when(springClientFactory).getClient(anyString(), + eq(OkHttpLoadBalancingClient.class)); + doReturn(clientConfig).when(springClientFactory).getClientConfig(anyString()); + OkHttpRibbonCommandFactory commandFactory = new OkHttpRibbonCommandFactory(springClientFactory, zuulProperties, new HashSet()); + RibbonCommandContext context = mock(RibbonCommandContext.class); + doReturn("service").when(context).getServiceId(); + OkHttpRibbonCommand ribbonCommand = commandFactory.create(context); + assertEquals(3600, ribbonCommand.getProperties().executionTimeoutInMilliseconds().get().intValue()); + } + + @Test + public void testRibbonCommandRetriesAndRibbonCommandTimeoutSetting() throws Exception { + ConfigurationManager.getConfigInstance().setProperty("service.ribbon.ConnectTimeout", 1000); + ConfigurationManager.getConfigInstance().setProperty("service.ribbon.ReadTimeout", 1000); + ConfigurationManager.getConfigInstance().setProperty("service.ribbon.MaxAutoRetries", 1); + ConfigurationManager.getConfigInstance().setProperty("service.ribbon.MaxAutoRetriesNextServer", 2); + SpringClientFactory springClientFactory = mock(SpringClientFactory.class); + ZuulProperties zuulProperties = new ZuulProperties(); + OkHttpLoadBalancingClient loadBalancingHttpClient = mock(OkHttpLoadBalancingClient.class); + IClientConfig clientConfig = new DefaultClientConfigImpl(); + clientConfig.set(IClientConfigKey.Keys.ConnectTimeout, 100); + clientConfig.set(IClientConfigKey.Keys.ReadTimeout, 500); + doReturn(loadBalancingHttpClient).when(springClientFactory).getClient(anyString(), + eq(OkHttpLoadBalancingClient.class)); + doReturn(clientConfig).when(springClientFactory).getClientConfig(anyString()); + OkHttpRibbonCommandFactory commandFactory = new OkHttpRibbonCommandFactory(springClientFactory, zuulProperties, new HashSet()); + RibbonCommandContext context = mock(RibbonCommandContext.class); + doReturn("service").when(context).getServiceId(); + OkHttpRibbonCommand ribbonCommand = commandFactory.create(context); + assertEquals(12000, ribbonCommand.getProperties().executionTimeoutInMilliseconds().get().intValue()); + } + + @Test + public void testRibbonCommandRetriesAndRibbonCommandTimeoutPartOfSetting() throws Exception { + ConfigurationManager.getConfigInstance().setProperty("service.ribbon.ConnectTimeout", 1000); + ConfigurationManager.getConfigInstance().setProperty("service.ribbon.MaxAutoRetries", 1); + SpringClientFactory springClientFactory = mock(SpringClientFactory.class); + ZuulProperties zuulProperties = new ZuulProperties(); + OkHttpLoadBalancingClient loadBalancingHttpClient = mock(OkHttpLoadBalancingClient.class); + IClientConfig clientConfig = new DefaultClientConfigImpl(); + clientConfig.set(IClientConfigKey.Keys.ConnectTimeout, 100); + clientConfig.set(IClientConfigKey.Keys.ReadTimeout, 500); + doReturn(loadBalancingHttpClient).when(springClientFactory).getClient(anyString(), + eq(OkHttpLoadBalancingClient.class)); + doReturn(clientConfig).when(springClientFactory).getClientConfig(anyString()); + OkHttpRibbonCommandFactory commandFactory = new OkHttpRibbonCommandFactory(springClientFactory, zuulProperties, new HashSet()); + RibbonCommandContext context = mock(RibbonCommandContext.class); + doReturn("service").when(context).getServiceId(); + OkHttpRibbonCommand ribbonCommand = commandFactory.create(context); + assertEquals(6000, ribbonCommand.getProperties().executionTimeoutInMilliseconds().get().intValue()); } } \ No newline at end of file