diff --git a/hystrix-contrib/hystrix-javanica/README.md b/hystrix-contrib/hystrix-javanica/README.md index 494adcdd5..b87744305 100644 --- a/hystrix-contrib/hystrix-javanica/README.md +++ b/hystrix-contrib/hystrix-javanica/README.md @@ -198,6 +198,25 @@ ConfigurationManager.getConfigInstance().setProperty("hystrix.command.getUserByI ``` More about Hystrix command properties [command](https://github.com/Netflix/Hystrix/wiki/Configuration#wiki-CommandExecution) and [fallback](https://github.com/Netflix/Hystrix/wiki/Configuration#wiki-CommandFallback) +ThreadPoolProperties can be set using @HystrixCommand's 'threadPoolProperties' like below: + +```java + @HystrixCommand(commandProperties = { + @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "500") + }, + threadPoolProperties = { + @HystrixProperty(name = "coreSize", value = "30"), + @HystrixProperty(name = "maxQueueSize", value = "101"), + @HystrixProperty(name = "keepAliveTimeMinutes", value = "2"), + @HystrixProperty(name = "queueSizeRejectionThreshold", value = "15"), + @HystrixProperty(name = "metrics.rollingStats.numBuckets", value = "12"), + @HystrixProperty(name = "metrics.rollingStats.timeInMilliseconds", value = "1440") + }) + public User getUserById(String id) { + return userResource.getUserById(id); + } +``` + ## Hystrix collapser Suppose you have some command which calls should be collapsed in one backend call. For this goal you can use ```@HystrixCollapser``` annotation. diff --git a/hystrix-contrib/hystrix-javanica/src/main/java/com/netflix/hystrix/contrib/javanica/annotation/HystrixCommand.java b/hystrix-contrib/hystrix-javanica/src/main/java/com/netflix/hystrix/contrib/javanica/annotation/HystrixCommand.java index 83386b9d2..92f3231be 100644 --- a/hystrix-contrib/hystrix-javanica/src/main/java/com/netflix/hystrix/contrib/javanica/annotation/HystrixCommand.java +++ b/hystrix-contrib/hystrix-javanica/src/main/java/com/netflix/hystrix/contrib/javanica/annotation/HystrixCommand.java @@ -100,6 +100,13 @@ */ HystrixProperty[] commandProperties() default {}; + /** + * Specifies thread pool properties. + * + * @return thread pool properties + */ + HystrixProperty[] threadPoolProperties() default {}; + /** * Defines exceptions which should be ignored and wrapped to throw in HystrixBadRequestException. * diff --git a/hystrix-contrib/hystrix-javanica/src/main/java/com/netflix/hystrix/contrib/javanica/command/AbstractHystrixCommandFactory.java b/hystrix-contrib/hystrix-javanica/src/main/java/com/netflix/hystrix/contrib/javanica/command/AbstractHystrixCommandFactory.java index d1413901e..4716232d7 100644 --- a/hystrix-contrib/hystrix-javanica/src/main/java/com/netflix/hystrix/contrib/javanica/command/AbstractHystrixCommandFactory.java +++ b/hystrix-contrib/hystrix-javanica/src/main/java/com/netflix/hystrix/contrib/javanica/command/AbstractHystrixCommandFactory.java @@ -18,9 +18,12 @@ import com.google.common.base.Throwables; import com.google.common.collect.Maps; +import com.netflix.config.ConfigurationManager; import com.netflix.hystrix.HystrixCollapser; +import com.netflix.hystrix.HystrixThreadPoolProperties; import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand; import com.netflix.hystrix.contrib.javanica.annotation.HystrixProperty; +import com.netflix.hystrix.contrib.javanica.conf.HystrixPropertiesManager; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.Validate; @@ -51,6 +54,8 @@ public T create(MetaHolder metaHolder, metaHolder.getHystrixCommand().commandKey() : metaHolder.getDefaultCommandKey(); + HystrixPropertiesManager.initializeThreadPoolProperties(metaHolder.getHystrixCommand()); + CommandSetterBuilder setterBuilder = new CommandSetterBuilder(); setterBuilder.commandKey(commandKey); setterBuilder.groupKey(groupKey); diff --git a/hystrix-contrib/hystrix-javanica/src/main/java/com/netflix/hystrix/contrib/javanica/command/CommandSetterBuilder.java b/hystrix-contrib/hystrix-javanica/src/main/java/com/netflix/hystrix/contrib/javanica/command/CommandSetterBuilder.java index d7b49f46b..264ce4606 100644 --- a/hystrix-contrib/hystrix-javanica/src/main/java/com/netflix/hystrix/contrib/javanica/command/CommandSetterBuilder.java +++ b/hystrix-contrib/hystrix-javanica/src/main/java/com/netflix/hystrix/contrib/javanica/command/CommandSetterBuilder.java @@ -19,6 +19,7 @@ import com.netflix.hystrix.HystrixCommandGroupKey; import com.netflix.hystrix.HystrixCommandKey; import com.netflix.hystrix.HystrixThreadPoolKey; +import com.netflix.hystrix.HystrixThreadPoolProperties; import org.apache.commons.lang3.StringUtils; /** @@ -29,6 +30,7 @@ public class CommandSetterBuilder { private String groupKey; private String commandKey; private String threadPoolKey; + private HystrixThreadPoolProperties.Setter threadPoolProperties = null; public CommandSetterBuilder groupKey(String pGroupKey) { this.groupKey = pGroupKey; @@ -50,11 +52,15 @@ public CommandSetterBuilder commandKey(String pCommandKey, String def) { return this; } + public CommandSetterBuilder threadPoolProperties(HystrixThreadPoolProperties.Setter threadPoolProperties) { + this.threadPoolProperties = threadPoolProperties; + return this; + } + public CommandSetterBuilder threadPoolKey(String pThreadPoolKey) { this.threadPoolKey = pThreadPoolKey; return this; } - /** * Creates instance of {@link HystrixCommand.Setter}. * @@ -67,6 +73,9 @@ public HystrixCommand.Setter build() { if (StringUtils.isNotBlank(threadPoolKey)) { setter.andThreadPoolKey(HystrixThreadPoolKey.Factory.asKey(threadPoolKey)); } + if (threadPoolProperties != null) { + setter.andThreadPoolPropertiesDefaults(threadPoolProperties); + } return setter; } diff --git a/hystrix-contrib/hystrix-javanica/src/main/java/com/netflix/hystrix/contrib/javanica/conf/HystrixPropertiesManager.java b/hystrix-contrib/hystrix-javanica/src/main/java/com/netflix/hystrix/contrib/javanica/conf/HystrixPropertiesManager.java index 3d8a5e0d5..585029698 100644 --- a/hystrix-contrib/hystrix-javanica/src/main/java/com/netflix/hystrix/contrib/javanica/conf/HystrixPropertiesManager.java +++ b/hystrix-contrib/hystrix-javanica/src/main/java/com/netflix/hystrix/contrib/javanica/conf/HystrixPropertiesManager.java @@ -17,6 +17,7 @@ import com.google.common.collect.Maps; import com.netflix.config.ConfigurationManager; +import com.netflix.hystrix.HystrixThreadPoolProperties; import com.netflix.hystrix.contrib.javanica.annotation.HystrixCollapser; import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand; import com.netflix.hystrix.contrib.javanica.annotation.HystrixProperty; @@ -96,4 +97,22 @@ private static void validate(HystrixProperty hystrixProperty) throws IllegalArgu Validate.notBlank(hystrixProperty.name(), "hystrix property name cannot be null"); } + public static void initializeThreadPoolProperties(HystrixCommand hystrixCommand) { + if(hystrixCommand.threadPoolProperties() == null || hystrixCommand.threadPoolProperties().length == 0) { + return; + } + + HystrixThreadPoolProperties.Setter setter = HystrixThreadPoolProperties.Setter(); + String threadPoolKey = hystrixCommand.threadPoolKey(); + if(threadPoolKey == null || "".equals(threadPoolKey)) { + threadPoolKey = "default"; + } + + HystrixProperty[] properties = hystrixCommand.threadPoolProperties(); + for(HystrixProperty property : properties) { + String name = String.format("hystrix.threadpool.%s.%s", threadPoolKey, property.name()); + ConfigurationManager.getConfigInstance().setProperty(name, property.value()); + } + } + } diff --git a/hystrix-contrib/hystrix-javanica/src/test/java/com/netflix/hystrix/contrib/javanica/test/spring/configuration/command/CommandPropertiesTest.java b/hystrix-contrib/hystrix-javanica/src/test/java/com/netflix/hystrix/contrib/javanica/test/spring/configuration/command/CommandPropertiesTest.java index 68c4002bf..11ab9dcd3 100644 --- a/hystrix-contrib/hystrix-javanica/src/test/java/com/netflix/hystrix/contrib/javanica/test/spring/configuration/command/CommandPropertiesTest.java +++ b/hystrix-contrib/hystrix-javanica/src/test/java/com/netflix/hystrix/contrib/javanica/test/spring/configuration/command/CommandPropertiesTest.java @@ -2,11 +2,14 @@ import com.netflix.hystrix.HystrixEventType; import com.netflix.hystrix.HystrixRequestLog; +import com.netflix.hystrix.HystrixThreadPool; +import com.netflix.hystrix.HystrixThreadPoolProperties; import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand; import com.netflix.hystrix.contrib.javanica.annotation.HystrixProperty; import com.netflix.hystrix.contrib.javanica.test.spring.conf.AopCglibConfig; import com.netflix.hystrix.contrib.javanica.test.spring.domain.User; import com.netflix.hystrix.strategy.concurrency.HystrixRequestContext; + import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; @@ -15,6 +18,9 @@ import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import java.lang.reflect.Field; +import java.util.concurrent.TimeUnit; + import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; @@ -26,7 +32,7 @@ public class CommandPropertiesTest { private UserService userService; @Test - public void testGetUser() { + public void testGetUser() throws NoSuchFieldException, IllegalAccessException { HystrixRequestContext context = HystrixRequestContext.initializeContext(); try { User u1 = userService.getUser("1", "name: "); @@ -41,6 +47,21 @@ public void testGetUser() { // assert properties assertEquals(110, command.getProperties().executionIsolationThreadTimeoutInMilliseconds().get().intValue()); assertEquals(false, command.getProperties().executionIsolationThreadInterruptOnTimeout().get()); + + Field field = command.getClass().getSuperclass().getSuperclass().getSuperclass().getDeclaredField("threadPool"); + field.setAccessible(true); + HystrixThreadPool threadPool = (HystrixThreadPool) field.get(command); + + Field field2 = HystrixThreadPool.HystrixThreadPoolDefault.class.getDeclaredField("properties"); + field2.setAccessible(true); + HystrixThreadPoolProperties properties = (HystrixThreadPoolProperties) field2.get(threadPool); + + assertEquals(30, (int) properties.coreSize().get()); + assertEquals(101, (int) properties.maxQueueSize().get()); + assertEquals(2, (int) properties.keepAliveTimeMinutes().get()); + assertEquals(15, (int) properties.queueSizeRejectionThreshold().get()); + assertEquals(1440, (int) properties.metricsRollingStatisticalWindowInMilliseconds().get()); + assertEquals(12, (int) properties.metricsRollingStatisticalWindowBuckets().get()); } finally { context.shutdown(); } @@ -70,6 +91,14 @@ public static class UserService { commandProperties = { @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "110"), @HystrixProperty(name = "execution.isolation.thread.interruptOnTimeout", value = "false") + }, + threadPoolProperties = { + @HystrixProperty(name = "coreSize", value = "30"), + @HystrixProperty(name = "maxQueueSize", value = "101"), + @HystrixProperty(name = "keepAliveTimeMinutes", value = "2"), + @HystrixProperty(name = "metrics.rollingStats.numBuckets", value = "12"), + @HystrixProperty(name = "queueSizeRejectionThreshold", value = "15"), + @HystrixProperty(name = "metrics.rollingStats.timeInMilliseconds", value = "1440") }) public User getUser(String id, String name) { return new User(id, name + id); // it should be network call