From 208f58104ae07a8168781528495ffa450773f28e Mon Sep 17 00:00:00 2001 From: cbono Date: Sat, 15 Feb 2020 08:11:15 -0600 Subject: [PATCH 1/2] Add 'threads' configuration group for embedded containers See gh-19475 --- .../autoconfigure/web/ServerProperties.java | 321 +++++++++++++----- .../JettyWebServerFactoryCustomizer.java | 19 +- .../TomcatWebServerFactoryCustomizer.java | 7 +- .../UndertowWebServerFactoryCustomizer.java | 5 +- .../web/ServerPropertiesTests.java | 136 +++++++- ...TomcatWebServerFactoryCustomizerTests.java | 4 +- ...dertowWebServerFactoryCustomizerTests.java | 16 + ...ervletWebServerFactoryCustomizerTests.java | 4 +- 8 files changed, 407 insertions(+), 105 deletions(-) diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/ServerProperties.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/ServerProperties.java index 67341b9da2..af8e3ab86d 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/ServerProperties.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/ServerProperties.java @@ -66,6 +66,7 @@ import org.springframework.util.unit.DataSize; * @author Dirk Deyne * @author HaiTao Zhang * @author Victor Mandujano + * @author Chris Bono * @since 1.0.0 */ @ConfigurationProperties(prefix = "server", ignoreUnknownFields = true) @@ -316,6 +317,11 @@ public class ServerProperties { */ private final Accesslog accesslog = new Accesslog(); + /** + * Thread related configuration. + */ + private final Threads threads = new Threads(); + /** * Tomcat base directory. If not specified, a temporary directory is used. */ @@ -328,16 +334,6 @@ public class ServerProperties { @DurationUnit(ChronoUnit.SECONDS) private Duration backgroundProcessorDelay = Duration.ofSeconds(10); - /** - * Maximum amount of worker threads. - */ - private int maxThreads = 200; - - /** - * Minimum amount of worker threads. - */ - private int minSpareThreads = 10; - /** * Maximum size of the form content in any HTTP post request. */ @@ -425,20 +421,26 @@ public class ServerProperties { */ private final Remoteip remoteip = new Remoteip(); + @Deprecated + @DeprecatedConfigurationProperty(replacement = "server.tomcat.threads.max") public int getMaxThreads() { - return this.maxThreads; + return this.getThreads().getMax(); } + @Deprecated public void setMaxThreads(int maxThreads) { - this.maxThreads = maxThreads; + this.getThreads().setMax(maxThreads); } + @Deprecated + @DeprecatedConfigurationProperty(replacement = "server.tomcat.threads.min-spare") public int getMinSpareThreads() { - return this.minSpareThreads; + return this.getThreads().getMinSpare(); } + @Deprecated public void setMinSpareThreads(int minSpareThreads) { - this.minSpareThreads = minSpareThreads; + this.getThreads().setMinSpare(minSpareThreads); } @Deprecated @@ -464,6 +466,10 @@ public class ServerProperties { return this.accesslog; } + public Threads getThreads() { + return this.threads; + } + public Duration getBackgroundProcessorDelay() { return this.backgroundProcessorDelay; } @@ -870,6 +876,39 @@ public class ServerProperties { } + /** + * Tomcat thread properties. + */ + public static class Threads { + + /** + * Maximum amount of worker threads. + */ + private int max = 200; + + /** + * Minimum amount of worker threads. + */ + private int minSpare = 10; + + public int getMax() { + return this.max; + } + + public void setMax(int max) { + this.max = max; + } + + public int getMinSpare() { + return this.minSpare; + } + + public void setMinSpare(int minSpare) { + this.minSpare = minSpare; + } + + } + /** * Tomcat static resource properties. */ @@ -1022,44 +1061,16 @@ public class ServerProperties { */ private final Accesslog accesslog = new Accesslog(); + /** + * Thread related configuration. + */ + private final Threads threads = new Threads(); + /** * Maximum size of the form content in any HTTP post request. */ private DataSize maxHttpFormPostSize = DataSize.ofBytes(200000); - /** - * Number of acceptor threads to use. When the value is -1, the default, the - * number of acceptors is derived from the operating environment. - */ - private Integer acceptors = -1; - - /** - * Number of selector threads to use. When the value is -1, the default, the - * number of selectors is derived from the operating environment. - */ - private Integer selectors = -1; - - /** - * Minimum number of threads. - */ - private int minThreads = 8; - - /** - * Maximum number of threads. - */ - private int maxThreads = 200; - - /** - * Maximum capacity of the thread pool's backing queue. A default is computed - * based on the threading configuration. - */ - private Integer maxQueueCapacity; - - /** - * Maximum thread idle time. - */ - private Duration threadIdleTimeout = Duration.ofMillis(60000); - /** * Time that the connection can be idle before it is closed. */ @@ -1069,6 +1080,10 @@ public class ServerProperties { return this.accesslog; } + public Threads getThreads() { + return this.threads; + } + @Deprecated @DeprecatedConfigurationProperty(replacement = "server.jetty.max-http-form-post-size") public DataSize getMaxHttpPostSize() { @@ -1088,52 +1103,68 @@ public class ServerProperties { this.maxHttpFormPostSize = maxHttpFormPostSize; } + @Deprecated + @DeprecatedConfigurationProperty(replacement = "server.jetty.threads.acceptors") public Integer getAcceptors() { - return this.acceptors; + return this.getThreads().getAcceptors(); } public void setAcceptors(Integer acceptors) { - this.acceptors = acceptors; + this.getThreads().setAcceptors(acceptors); } + @Deprecated + @DeprecatedConfigurationProperty(replacement = "server.jetty.threads.selectors") public Integer getSelectors() { - return this.selectors; + return this.getThreads().getSelectors(); } public void setSelectors(Integer selectors) { - this.selectors = selectors; + this.getThreads().setSelectors(selectors); } - public void setMinThreads(int minThreads) { - this.minThreads = minThreads; + @Deprecated + @DeprecatedConfigurationProperty(replacement = "server.jetty.threads.min") + public Integer getMinThreads() { + return this.getThreads().getMin(); } - public int getMinThreads() { - return this.minThreads; + @Deprecated + public void setMinThreads(Integer minThreads) { + this.getThreads().setMin(minThreads); } - public void setMaxThreads(int maxThreads) { - this.maxThreads = maxThreads; + @Deprecated + @DeprecatedConfigurationProperty(replacement = "server.jetty.threads.max") + public Integer getMaxThreads() { + return this.getThreads().getMax(); } - public int getMaxThreads() { - return this.maxThreads; - } - - public void setMaxQueueCapacity(Integer maxQueueCapacity) { - this.maxQueueCapacity = maxQueueCapacity; + @Deprecated + public void setMaxThreads(Integer maxThreads) { + this.getThreads().setMax(maxThreads); } + @Deprecated + @DeprecatedConfigurationProperty(replacement = "server.jetty.threads.maxQueueCapacity") public Integer getMaxQueueCapacity() { - return this.maxQueueCapacity; + return this.getThreads().getMaxQueueCapacity(); } - public void setThreadIdleTimeout(Duration threadIdleTimeout) { - this.threadIdleTimeout = threadIdleTimeout; + @Deprecated + public void setMaxQueueCapacity(Integer maxQueueCapacity) { + this.getThreads().setMaxQueueCapacity(maxQueueCapacity); } + @Deprecated + @DeprecatedConfigurationProperty(replacement = "server.jetty.threads.idle-timeout") public Duration getThreadIdleTimeout() { - return this.threadIdleTimeout; + return this.getThreads().getIdleTimeout(); + } + + @Deprecated + public void setThreadIdleTimeout(Duration threadIdleTimeout) { + this.getThreads().setIdleTimeout(threadIdleTimeout); } public Duration getConnectionIdleTimeout() { @@ -1274,6 +1305,94 @@ public class ServerProperties { } + /** + * Jetty thread properties. + */ + public static class Threads { + + /** + * Number of acceptor threads to use. When the value is -1, the default, the + * number of acceptors is derived from the operating environment. + */ + private Integer acceptors = -1; + + /** + * Number of selector threads to use. When the value is -1, the default, the + * number of selectors is derived from the operating environment. + */ + private Integer selectors = -1; + + /** + * Maximum number of threads. + */ + private Integer max = 200; + + /** + * Minimum number of threads. + */ + private Integer min = 8; + + /** + * Maximum capacity of the thread pool's backing queue. A default is computed + * based on the threading configuration. + */ + private Integer maxQueueCapacity; + + /** + * Maximum thread idle time. + */ + private Duration idleTimeout = Duration.ofMillis(60000); + + public Integer getAcceptors() { + return this.acceptors; + } + + public void setAcceptors(Integer acceptors) { + this.acceptors = acceptors; + } + + public Integer getSelectors() { + return this.selectors; + } + + public void setSelectors(Integer selectors) { + this.selectors = selectors; + } + + public void setMin(Integer min) { + this.min = min; + } + + public Integer getMin() { + return this.min; + } + + public void setMax(Integer max) { + this.max = max; + } + + public Integer getMax() { + return this.max; + } + + public Integer getMaxQueueCapacity() { + return this.maxQueueCapacity; + } + + public void setMaxQueueCapacity(Integer maxQueueCapacity) { + this.maxQueueCapacity = maxQueueCapacity; + } + + public void setIdleTimeout(Duration idleTimeout) { + this.idleTimeout = idleTimeout; + } + + public Duration getIdleTimeout() { + return this.idleTimeout; + } + + } + } /** @@ -1313,17 +1432,6 @@ public class ServerProperties { */ private DataSize bufferSize; - /** - * Number of I/O threads to create for the worker. The default is derived from the - * number of available processors. - */ - private Integer ioThreads; - - /** - * Number of worker threads. The default is 8 times the number of I/O threads. - */ - private Integer workerThreads; - /** * Whether to allocate buffers outside the Java heap. The default is derived from * the maximum amount of memory that is available to the JVM. @@ -1386,6 +1494,11 @@ public class ServerProperties { private final Accesslog accesslog = new Accesslog(); + /** + * Thread related configuration. + */ + private final Threads threads = new Threads(); + private final Options options = new Options(); public DataSize getMaxHttpPostSize() { @@ -1404,20 +1517,26 @@ public class ServerProperties { this.bufferSize = bufferSize; } + @Deprecated + @DeprecatedConfigurationProperty(replacement = "server.undertow.threads.io") public Integer getIoThreads() { - return this.ioThreads; + return this.getThreads().getIo(); } + @Deprecated public void setIoThreads(Integer ioThreads) { - this.ioThreads = ioThreads; + this.getThreads().setIo(ioThreads); } + @Deprecated + @DeprecatedConfigurationProperty(replacement = "server.undertow.threads.worker") public Integer getWorkerThreads() { - return this.workerThreads; + return this.getThreads().getWorker(); } + @Deprecated public void setWorkerThreads(Integer workerThreads) { - this.workerThreads = workerThreads; + this.getThreads().setWorker(workerThreads); } public Boolean getDirectBuffers() { @@ -1504,6 +1623,10 @@ public class ServerProperties { return this.accesslog; } + public Threads getThreads() { + return this.threads; + } + public Options getOptions() { return this.options; } @@ -1593,6 +1716,40 @@ public class ServerProperties { } + /** + * Undertow thread properties. + */ + public static class Threads { + + /** + * Number of I/O threads to create for the worker. The default is derived from + * the number of available processors. + */ + private Integer io; + + /** + * Number of worker threads. The default is 8 times the number of I/O threads. + */ + private Integer worker; + + public Integer getIo() { + return this.io; + } + + public void setIo(Integer io) { + this.io = io; + } + + public Integer getWorker() { + return this.worker; + } + + public void setWorker(Integer worker) { + this.worker = worker; + } + + } + public static class Options { private Map socket = new LinkedHashMap<>(); diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/embedded/JettyWebServerFactoryCustomizer.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/embedded/JettyWebServerFactoryCustomizer.java index 54f3b42e08..7f55f5f944 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/embedded/JettyWebServerFactoryCustomizer.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/embedded/JettyWebServerFactoryCustomizer.java @@ -76,11 +76,12 @@ public class JettyWebServerFactoryCustomizer public void customize(ConfigurableJettyWebServerFactory factory) { ServerProperties properties = this.serverProperties; ServerProperties.Jetty jettyProperties = properties.getJetty(); + ServerProperties.Jetty.Threads threadProperties = jettyProperties.getThreads(); factory.setUseForwardHeaders(getOrDeduceUseForwardHeaders()); - factory.setThreadPool(determineThreadPool(jettyProperties)); + factory.setThreadPool(determineThreadPool(jettyProperties.getThreads())); PropertyMapper propertyMapper = PropertyMapper.get(); - propertyMapper.from(jettyProperties::getAcceptors).whenNonNull().to(factory::setAcceptors); - propertyMapper.from(jettyProperties::getSelectors).whenNonNull().to(factory::setSelectors); + propertyMapper.from(threadProperties::getAcceptors).whenNonNull().to(factory::setAcceptors); + propertyMapper.from(threadProperties::getSelectors).whenNonNull().to(factory::setSelectors); propertyMapper.from(properties::getMaxHttpHeaderSize).whenNonNull().asInt(DataSize::toBytes) .when(this::isPositive).to((maxHttpHeaderSize) -> factory .addServerCustomizers(new MaxHttpHeaderSizeCustomizer(maxHttpHeaderSize))); @@ -141,12 +142,12 @@ public class JettyWebServerFactoryCustomizer }); } - private ThreadPool determineThreadPool(ServerProperties.Jetty properties) { - BlockingQueue queue = determineBlockingQueue(properties.getMaxQueueCapacity()); - int maxThreadCount = (properties.getMaxThreads() > 0) ? properties.getMaxThreads() : 200; - int minThreadCount = (properties.getMinThreads() > 0) ? properties.getMinThreads() : 8; - int threadIdleTimeout = (properties.getThreadIdleTimeout() != null) - ? (int) properties.getThreadIdleTimeout().toMillis() : 60000; + private ThreadPool determineThreadPool(ServerProperties.Jetty.Threads threadProperties) { + BlockingQueue queue = determineBlockingQueue(threadProperties.getMaxQueueCapacity()); + int maxThreadCount = (threadProperties.getMax() > 0) ? threadProperties.getMax() : 200; + int minThreadCount = (threadProperties.getMin() > 0) ? threadProperties.getMin() : 8; + int threadIdleTimeout = (threadProperties.getIdleTimeout() != null) + ? (int) threadProperties.getIdleTimeout().toMillis() : 60000; return new QueuedThreadPool(maxThreadCount, minThreadCount, threadIdleTimeout, queue); } diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/embedded/TomcatWebServerFactoryCustomizer.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/embedded/TomcatWebServerFactoryCustomizer.java index 2228c6d648..1692619038 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/embedded/TomcatWebServerFactoryCustomizer.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/embedded/TomcatWebServerFactoryCustomizer.java @@ -79,14 +79,15 @@ public class TomcatWebServerFactoryCustomizer public void customize(ConfigurableTomcatWebServerFactory factory) { ServerProperties properties = this.serverProperties; ServerProperties.Tomcat tomcatProperties = properties.getTomcat(); + ServerProperties.Tomcat.Threads threadProperties = tomcatProperties.getThreads(); PropertyMapper propertyMapper = PropertyMapper.get(); propertyMapper.from(tomcatProperties::getBasedir).whenNonNull().to(factory::setBaseDirectory); propertyMapper.from(tomcatProperties::getBackgroundProcessorDelay).whenNonNull().as(Duration::getSeconds) .as(Long::intValue).to(factory::setBackgroundProcessorDelay); customizeRemoteIpValve(factory); - propertyMapper.from(tomcatProperties::getMaxThreads).when(this::isPositive) - .to((maxThreads) -> customizeMaxThreads(factory, tomcatProperties.getMaxThreads())); - propertyMapper.from(tomcatProperties::getMinSpareThreads).when(this::isPositive) + propertyMapper.from(threadProperties::getMax).when(this::isPositive) + .to((maxThreads) -> customizeMaxThreads(factory, threadProperties.getMax())); + propertyMapper.from(threadProperties::getMinSpare).when(this::isPositive) .to((minSpareThreads) -> customizeMinThreads(factory, minSpareThreads)); propertyMapper.from(this.serverProperties.getMaxHttpHeaderSize()).whenNonNull().asInt(DataSize::toBytes) .when(this::isPositive) diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/embedded/UndertowWebServerFactoryCustomizer.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/embedded/UndertowWebServerFactoryCustomizer.java index d689b9e9f2..e904f1c8fd 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/embedded/UndertowWebServerFactoryCustomizer.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/embedded/UndertowWebServerFactoryCustomizer.java @@ -88,9 +88,10 @@ public class UndertowWebServerFactoryCustomizer private void mapUndertowProperties(ConfigurableUndertowWebServerFactory factory, FactoryOptions options) { PropertyMapper map = PropertyMapper.get().alwaysApplyingWhenNonNull(); Undertow properties = this.serverProperties.getUndertow(); + ServerProperties.Undertow.Threads threadProperties = properties.getThreads(); map.from(properties::getBufferSize).whenNonNull().asInt(DataSize::toBytes).to(factory::setBufferSize); - map.from(properties::getIoThreads).to(factory::setIoThreads); - map.from(properties::getWorkerThreads).to(factory::setWorkerThreads); + map.from(threadProperties::getIo).to(factory::setIoThreads); + map.from(threadProperties::getWorker).to(factory::setWorkerThreads); map.from(properties::getDirectBuffers).to(factory::setUseDirectBuffers); map.from(properties::getMaxHttpPostSize).as(DataSize::toBytes).when(this::isPositive) .to(options.server(UndertowOptions.MAX_ENTITY_SIZE)); diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/ServerPropertiesTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/ServerPropertiesTests.java index 8caebfdbf2..4759605c86 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/ServerPropertiesTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/ServerPropertiesTests.java @@ -20,6 +20,7 @@ import java.io.IOException; import java.net.InetAddress; import java.net.URI; import java.nio.charset.StandardCharsets; +import java.time.Duration; import java.util.Collections; import java.util.HashMap; import java.util.Map; @@ -75,6 +76,7 @@ import static org.assertj.core.api.Assertions.assertThat; * @author Andrew McGhie * @author HaiTao Zhang * @author Rafiullah Hamedy + * @author Chris Bono */ class ServerPropertiesTests { @@ -207,40 +209,132 @@ class ServerPropertiesTests { assertThat(this.properties.getMaxHttpHeaderSize()).isEqualTo(DataSize.ofKilobytes(1)); } + @Test + void testCustomizeTomcatMaxThreads() { + bind("server.tomcat.threads.max", "10"); + assertThat(this.properties.getTomcat().getThreads().getMax()).isEqualTo(10); + } + + @Deprecated + @Test + void testCustomizeTomcatMaxThreadsDeprecated() { + bind("server.tomcat.maxThreads", "10"); + assertThat(this.properties.getTomcat().getMaxThreads()).isEqualTo(10); + // Verify they are locked on same backing props to avoid further downstream + // deprecated testing + assertThat(this.properties.getTomcat().getThreads().getMax()).isEqualTo(10); + } + + @Test + void testCustomizeTomcatMinSpareThreads() { + bind("server.tomcat.threads.min-spare", "10"); + assertThat(this.properties.getTomcat().getThreads().getMinSpare()).isEqualTo(10); + } + + @Deprecated + @Test + void testCustomizeTomcatMinSpareThreadsDeprecated() { + bind("server.tomcat.min-spare-threads", "10"); + assertThat(this.properties.getTomcat().getMinSpareThreads()).isEqualTo(10); + // Verify they are locked on same backing props to avoid further downstream + // deprecated testing + assertThat(this.properties.getTomcat().getThreads().getMinSpare()).isEqualTo(10); + } + @Test void testCustomizeJettyAcceptors() { + bind("server.jetty.threads.acceptors", "10"); + assertThat(this.properties.getJetty().getThreads().getAcceptors()).isEqualTo(10); + } + + @Deprecated + @Test + void testCustomizeJettyAcceptorsDeprecated() { bind("server.jetty.acceptors", "10"); assertThat(this.properties.getJetty().getAcceptors()).isEqualTo(10); + // Verify they are locked on same backing props to avoid further downstream + // deprecated testing + assertThat(this.properties.getJetty().getThreads().getAcceptors()).isEqualTo(10); } @Test void testCustomizeJettySelectors() { + bind("server.jetty.threads.selectors", "10"); + assertThat(this.properties.getJetty().getThreads().getSelectors()).isEqualTo(10); + } + + @Deprecated + @Test + void testCustomizeJettySelectorsDeprecated() { bind("server.jetty.selectors", "10"); assertThat(this.properties.getJetty().getSelectors()).isEqualTo(10); + // Verify they are locked on same backing props to avoid further downstream + // deprecated testing + assertThat(this.properties.getJetty().getThreads().getSelectors()).isEqualTo(10); } @Test void testCustomizeJettyMaxThreads() { - bind("server.jetty.max-threads", "10"); + bind("server.jetty.threads.max", "10"); + assertThat(this.properties.getJetty().getThreads().getMax()).isEqualTo(10); + } + + @Deprecated + @Test + void testCustomizeJettyMaxThreadsDeprecated() { + bind("server.jetty.maxThreads", "10"); assertThat(this.properties.getJetty().getMaxThreads()).isEqualTo(10); + // Verify they are locked on same backing props to avoid further downstream + // deprecated testing + assertThat(this.properties.getJetty().getThreads().getMax()).isEqualTo(10); } @Test void testCustomizeJettyMinThreads() { - bind("server.jetty.min-threads", "10"); + bind("server.jetty.threads.min", "10"); + assertThat(this.properties.getJetty().getThreads().getMin()).isEqualTo(10); + } + + @Deprecated + @Test + void testCustomizeJettyMinThreadsDeprecated() { + bind("server.jetty.minThreads", "10"); assertThat(this.properties.getJetty().getMinThreads()).isEqualTo(10); + // Verify they are locked on same backing props to avoid further downstream + // deprecated testing + assertThat(this.properties.getJetty().getThreads().getMin()).isEqualTo(10); } @Test void testCustomizeJettyIdleTimeout() { + bind("server.jetty.threads.idle-timeout", "10s"); + assertThat(this.properties.getJetty().getThreads().getIdleTimeout()).isEqualTo(Duration.ofSeconds(10)); + } + + @Deprecated + @Test + void testCustomizeJettyIdleTimeoutDeprecated() { bind("server.jetty.thread-idle-timeout", "10s"); - assertThat(this.properties.getJetty().getThreadIdleTimeout()).hasSeconds(10); + assertThat(this.properties.getJetty().getThreadIdleTimeout()).isEqualTo(Duration.ofSeconds(10)); + // Verify they are locked on same backing props to avoid further downstream + // deprecated testing + assertThat(this.properties.getJetty().getThreads().getIdleTimeout()).hasSeconds(10); } @Test void testCustomizeJettyMaxQueueCapacity() { + bind("server.jetty.threads.max-queue-capacity", "5150"); + assertThat(this.properties.getJetty().getThreads().getMaxQueueCapacity()).isEqualTo(5150); + } + + @Deprecated + @Test + void testCustomizeJettyMaxQueueCapacityDeprecated() { bind("server.jetty.max-queue-capacity", "5150"); assertThat(this.properties.getJetty().getMaxQueueCapacity()).isEqualTo(5150); + // Verify they are locked on same backing props to avoid further downstream + // deprecated testing + assertThat(this.properties.getJetty().getThreads().getMaxQueueCapacity()).isEqualTo(5150); } @Test @@ -257,6 +351,38 @@ class ServerPropertiesTests { "true"); } + @Test + void testCustomizeUndertowIoThreads() { + bind("server.undertow.threads.io", "4"); + assertThat(this.properties.getUndertow().getThreads().getIo()).isEqualTo(4); + } + + @Deprecated + @Test + void testCustomizeUndertowIoThreadsDeprecated() { + bind("server.undertow.ioThreads", "4"); + assertThat(this.properties.getUndertow().getIoThreads()).isEqualTo(4); + // Verify they are locked on same backing props to avoid further downstream + // deprecated testing + assertThat(this.properties.getUndertow().getThreads().getIo()).isEqualTo(4); + } + + @Test + void testCustomizeUndertowWorkerThreads() { + bind("server.undertow.threads.worker", "10"); + assertThat(this.properties.getUndertow().getThreads().getWorker()).isEqualTo(10); + } + + @Deprecated + @Test + void testCustomizeUndertowWorkerThreadsDeprecated() { + bind("server.undertow.workerThreads", "10"); + assertThat(this.properties.getUndertow().getWorkerThreads()).isEqualTo(10); + // Verify they are locked on same backing props to avoid further downstream + // deprecated testing + assertThat(this.properties.getUndertow().getThreads().getWorker()).isEqualTo(10); + } + @Test void testCustomizeJettyAccessLog() { Map map = new HashMap<>(); @@ -295,12 +421,12 @@ class ServerPropertiesTests { @Test void tomcatMaxThreadsMatchesProtocolDefault() throws Exception { - assertThat(this.properties.getTomcat().getMaxThreads()).isEqualTo(getDefaultProtocol().getMaxThreads()); + assertThat(this.properties.getTomcat().getThreads().getMax()).isEqualTo(getDefaultProtocol().getMaxThreads()); } @Test void tomcatMinSpareThreadsMatchesProtocolDefault() throws Exception { - assertThat(this.properties.getTomcat().getMinSpareThreads()) + assertThat(this.properties.getTomcat().getThreads().getMinSpare()) .isEqualTo(getDefaultProtocol().getMinSpareThreads()); } diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/embedded/TomcatWebServerFactoryCustomizerTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/embedded/TomcatWebServerFactoryCustomizerTests.java index cb6d9b8c47..c11bcc87b4 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/embedded/TomcatWebServerFactoryCustomizerTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/embedded/TomcatWebServerFactoryCustomizerTests.java @@ -345,8 +345,8 @@ class TomcatWebServerFactoryCustomizerTests { @Test void testCustomizeMinSpareThreads() { - bind("server.tomcat.min-spare-threads=10"); - assertThat(this.serverProperties.getTomcat().getMinSpareThreads()).isEqualTo(10); + bind("server.tomcat.threads.min-spare=10"); + assertThat(this.serverProperties.getTomcat().getThreads().getMinSpare()).isEqualTo(10); } @Test diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/embedded/UndertowWebServerFactoryCustomizerTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/embedded/UndertowWebServerFactoryCustomizerTests.java index 2186844f78..1142e320b6 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/embedded/UndertowWebServerFactoryCustomizerTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/embedded/UndertowWebServerFactoryCustomizerTests.java @@ -132,6 +132,22 @@ class UndertowWebServerFactoryCustomizerTests { assertThat(boundServerOption(UndertowOptions.MAX_COOKIES)).isEqualTo(4); } + @Test + void customizeIoThreads() { + bind("server.undertow.threads.io=4"); + ConfigurableUndertowWebServerFactory factory = mock(ConfigurableUndertowWebServerFactory.class); + this.customizer.customize(factory); + verify(factory).setIoThreads(4); + } + + @Test + void customizeWorkerThreads() { + bind("server.undertow.threads.worker=10"); + ConfigurableUndertowWebServerFactory factory = mock(ConfigurableUndertowWebServerFactory.class); + this.customizer.customize(factory); + verify(factory).setWorkerThreads(10); + } + @Test void allowEncodedSlashes() { bind("server.undertow.allow-encoded-slash=true"); diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/servlet/ServletWebServerFactoryCustomizerTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/servlet/ServletWebServerFactoryCustomizerTests.java index ef6d3e0bf2..1af9c729ba 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/servlet/ServletWebServerFactoryCustomizerTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/servlet/ServletWebServerFactoryCustomizerTests.java @@ -139,9 +139,9 @@ class ServletWebServerFactoryCustomizerTests { @Test void testCustomizeTomcatMinSpareThreads() { Map map = new HashMap<>(); - map.put("server.tomcat.min-spare-threads", "10"); + map.put("server.tomcat.threads.min-spare", "10"); bindProperties(map); - assertThat(this.properties.getTomcat().getMinSpareThreads()).isEqualTo(10); + assertThat(this.properties.getTomcat().getThreads().getMinSpare()).isEqualTo(10); } @Test From 5893786cbb96fef962be95b70f9d7d2b0737ef9e Mon Sep 17 00:00:00 2001 From: Stephane Nicoll Date: Thu, 19 Mar 2020 12:12:27 +0100 Subject: [PATCH 2/2] Polish "Add 'threads' configuration group for embedded containers" See gh-19475 --- .../autoconfigure/web/ServerProperties.java | 2 +- .../JettyWebServerFactoryCustomizer.java | 14 ++++----- .../TomcatWebServerFactoryCustomizer.java | 2 +- .../UndertowWebServerFactoryCustomizer.java | 2 +- .../web/ServerPropertiesTests.java | 29 ------------------- .../JettyWebServerFactoryCustomizerTests.java | 14 ++++----- .../src/main/resources/application.properties | 2 +- 7 files changed, 18 insertions(+), 47 deletions(-) diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/ServerProperties.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/ServerProperties.java index af8e3ab86d..1b254ae21c 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/ServerProperties.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/ServerProperties.java @@ -1146,7 +1146,7 @@ public class ServerProperties { } @Deprecated - @DeprecatedConfigurationProperty(replacement = "server.jetty.threads.maxQueueCapacity") + @DeprecatedConfigurationProperty(replacement = "server.jetty.threads.max-queue-capacity") public Integer getMaxQueueCapacity() { return this.getThreads().getMaxQueueCapacity(); } diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/embedded/JettyWebServerFactoryCustomizer.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/embedded/JettyWebServerFactoryCustomizer.java index 7f55f5f944..ba8b2e2681 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/embedded/JettyWebServerFactoryCustomizer.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/embedded/JettyWebServerFactoryCustomizer.java @@ -76,8 +76,8 @@ public class JettyWebServerFactoryCustomizer public void customize(ConfigurableJettyWebServerFactory factory) { ServerProperties properties = this.serverProperties; ServerProperties.Jetty jettyProperties = properties.getJetty(); - ServerProperties.Jetty.Threads threadProperties = jettyProperties.getThreads(); factory.setUseForwardHeaders(getOrDeduceUseForwardHeaders()); + ServerProperties.Jetty.Threads threadProperties = jettyProperties.getThreads(); factory.setThreadPool(determineThreadPool(jettyProperties.getThreads())); PropertyMapper propertyMapper = PropertyMapper.get(); propertyMapper.from(threadProperties::getAcceptors).whenNonNull().to(factory::setAcceptors); @@ -142,12 +142,12 @@ public class JettyWebServerFactoryCustomizer }); } - private ThreadPool determineThreadPool(ServerProperties.Jetty.Threads threadProperties) { - BlockingQueue queue = determineBlockingQueue(threadProperties.getMaxQueueCapacity()); - int maxThreadCount = (threadProperties.getMax() > 0) ? threadProperties.getMax() : 200; - int minThreadCount = (threadProperties.getMin() > 0) ? threadProperties.getMin() : 8; - int threadIdleTimeout = (threadProperties.getIdleTimeout() != null) - ? (int) threadProperties.getIdleTimeout().toMillis() : 60000; + private ThreadPool determineThreadPool(ServerProperties.Jetty.Threads properties) { + BlockingQueue queue = determineBlockingQueue(properties.getMaxQueueCapacity()); + int maxThreadCount = (properties.getMax() > 0) ? properties.getMax() : 200; + int minThreadCount = (properties.getMin() > 0) ? properties.getMin() : 8; + int threadIdleTimeout = (properties.getIdleTimeout() != null) ? (int) properties.getIdleTimeout().toMillis() + : 60000; return new QueuedThreadPool(maxThreadCount, minThreadCount, threadIdleTimeout, queue); } diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/embedded/TomcatWebServerFactoryCustomizer.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/embedded/TomcatWebServerFactoryCustomizer.java index 1692619038..fff95b358d 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/embedded/TomcatWebServerFactoryCustomizer.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/embedded/TomcatWebServerFactoryCustomizer.java @@ -79,12 +79,12 @@ public class TomcatWebServerFactoryCustomizer public void customize(ConfigurableTomcatWebServerFactory factory) { ServerProperties properties = this.serverProperties; ServerProperties.Tomcat tomcatProperties = properties.getTomcat(); - ServerProperties.Tomcat.Threads threadProperties = tomcatProperties.getThreads(); PropertyMapper propertyMapper = PropertyMapper.get(); propertyMapper.from(tomcatProperties::getBasedir).whenNonNull().to(factory::setBaseDirectory); propertyMapper.from(tomcatProperties::getBackgroundProcessorDelay).whenNonNull().as(Duration::getSeconds) .as(Long::intValue).to(factory::setBackgroundProcessorDelay); customizeRemoteIpValve(factory); + ServerProperties.Tomcat.Threads threadProperties = tomcatProperties.getThreads(); propertyMapper.from(threadProperties::getMax).when(this::isPositive) .to((maxThreads) -> customizeMaxThreads(factory, threadProperties.getMax())); propertyMapper.from(threadProperties::getMinSpare).when(this::isPositive) diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/embedded/UndertowWebServerFactoryCustomizer.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/embedded/UndertowWebServerFactoryCustomizer.java index e904f1c8fd..01e71a3b5e 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/embedded/UndertowWebServerFactoryCustomizer.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/embedded/UndertowWebServerFactoryCustomizer.java @@ -88,8 +88,8 @@ public class UndertowWebServerFactoryCustomizer private void mapUndertowProperties(ConfigurableUndertowWebServerFactory factory, FactoryOptions options) { PropertyMapper map = PropertyMapper.get().alwaysApplyingWhenNonNull(); Undertow properties = this.serverProperties.getUndertow(); - ServerProperties.Undertow.Threads threadProperties = properties.getThreads(); map.from(properties::getBufferSize).whenNonNull().asInt(DataSize::toBytes).to(factory::setBufferSize); + ServerProperties.Undertow.Threads threadProperties = properties.getThreads(); map.from(threadProperties::getIo).to(factory::setIoThreads); map.from(threadProperties::getWorker).to(factory::setWorkerThreads); map.from(properties::getDirectBuffers).to(factory::setUseDirectBuffers); diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/ServerPropertiesTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/ServerPropertiesTests.java index 4759605c86..1d89156391 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/ServerPropertiesTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/ServerPropertiesTests.java @@ -219,9 +219,6 @@ class ServerPropertiesTests { @Test void testCustomizeTomcatMaxThreadsDeprecated() { bind("server.tomcat.maxThreads", "10"); - assertThat(this.properties.getTomcat().getMaxThreads()).isEqualTo(10); - // Verify they are locked on same backing props to avoid further downstream - // deprecated testing assertThat(this.properties.getTomcat().getThreads().getMax()).isEqualTo(10); } @@ -235,9 +232,6 @@ class ServerPropertiesTests { @Test void testCustomizeTomcatMinSpareThreadsDeprecated() { bind("server.tomcat.min-spare-threads", "10"); - assertThat(this.properties.getTomcat().getMinSpareThreads()).isEqualTo(10); - // Verify they are locked on same backing props to avoid further downstream - // deprecated testing assertThat(this.properties.getTomcat().getThreads().getMinSpare()).isEqualTo(10); } @@ -251,9 +245,6 @@ class ServerPropertiesTests { @Test void testCustomizeJettyAcceptorsDeprecated() { bind("server.jetty.acceptors", "10"); - assertThat(this.properties.getJetty().getAcceptors()).isEqualTo(10); - // Verify they are locked on same backing props to avoid further downstream - // deprecated testing assertThat(this.properties.getJetty().getThreads().getAcceptors()).isEqualTo(10); } @@ -268,8 +259,6 @@ class ServerPropertiesTests { void testCustomizeJettySelectorsDeprecated() { bind("server.jetty.selectors", "10"); assertThat(this.properties.getJetty().getSelectors()).isEqualTo(10); - // Verify they are locked on same backing props to avoid further downstream - // deprecated testing assertThat(this.properties.getJetty().getThreads().getSelectors()).isEqualTo(10); } @@ -283,9 +272,6 @@ class ServerPropertiesTests { @Test void testCustomizeJettyMaxThreadsDeprecated() { bind("server.jetty.maxThreads", "10"); - assertThat(this.properties.getJetty().getMaxThreads()).isEqualTo(10); - // Verify they are locked on same backing props to avoid further downstream - // deprecated testing assertThat(this.properties.getJetty().getThreads().getMax()).isEqualTo(10); } @@ -299,9 +285,6 @@ class ServerPropertiesTests { @Test void testCustomizeJettyMinThreadsDeprecated() { bind("server.jetty.minThreads", "10"); - assertThat(this.properties.getJetty().getMinThreads()).isEqualTo(10); - // Verify they are locked on same backing props to avoid further downstream - // deprecated testing assertThat(this.properties.getJetty().getThreads().getMin()).isEqualTo(10); } @@ -315,9 +298,6 @@ class ServerPropertiesTests { @Test void testCustomizeJettyIdleTimeoutDeprecated() { bind("server.jetty.thread-idle-timeout", "10s"); - assertThat(this.properties.getJetty().getThreadIdleTimeout()).isEqualTo(Duration.ofSeconds(10)); - // Verify they are locked on same backing props to avoid further downstream - // deprecated testing assertThat(this.properties.getJetty().getThreads().getIdleTimeout()).hasSeconds(10); } @@ -331,9 +311,6 @@ class ServerPropertiesTests { @Test void testCustomizeJettyMaxQueueCapacityDeprecated() { bind("server.jetty.max-queue-capacity", "5150"); - assertThat(this.properties.getJetty().getMaxQueueCapacity()).isEqualTo(5150); - // Verify they are locked on same backing props to avoid further downstream - // deprecated testing assertThat(this.properties.getJetty().getThreads().getMaxQueueCapacity()).isEqualTo(5150); } @@ -361,9 +338,6 @@ class ServerPropertiesTests { @Test void testCustomizeUndertowIoThreadsDeprecated() { bind("server.undertow.ioThreads", "4"); - assertThat(this.properties.getUndertow().getIoThreads()).isEqualTo(4); - // Verify they are locked on same backing props to avoid further downstream - // deprecated testing assertThat(this.properties.getUndertow().getThreads().getIo()).isEqualTo(4); } @@ -377,9 +351,6 @@ class ServerPropertiesTests { @Test void testCustomizeUndertowWorkerThreadsDeprecated() { bind("server.undertow.workerThreads", "10"); - assertThat(this.properties.getUndertow().getWorkerThreads()).isEqualTo(10); - // Verify they are locked on same backing props to avoid further downstream - // deprecated testing assertThat(this.properties.getUndertow().getThreads().getWorker()).isEqualTo(10); } diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/embedded/JettyWebServerFactoryCustomizerTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/embedded/JettyWebServerFactoryCustomizerTests.java index 4a1c989d0e..d685127d91 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/embedded/JettyWebServerFactoryCustomizerTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/embedded/JettyWebServerFactoryCustomizerTests.java @@ -179,7 +179,7 @@ class JettyWebServerFactoryCustomizerTests { @Test void threadPoolIdleTimeoutCanBeCustomized() { - bind("server.jetty.thread-idle-timeout=100s"); + bind("server.jetty.threads.idle-timeout=100s"); JettyWebServer server = customizeAndGetServer(); QueuedThreadPool threadPool = (QueuedThreadPool) server.getServer().getThreadPool(); assertThat(threadPool.getIdleTimeout()).isEqualTo(100000); @@ -187,7 +187,7 @@ class JettyWebServerFactoryCustomizerTests { @Test void threadPoolWithMaxQueueCapacityEqualToZeroCreateSynchronousQueue() { - bind("server.jetty.max-queue-capacity=0"); + bind("server.jetty.threads.max-queue-capacity=0"); JettyWebServer server = customizeAndGetServer(); ThreadPool threadPool = server.getServer().getThreadPool(); BlockingQueue queue = getQueue(threadPool); @@ -197,8 +197,8 @@ class JettyWebServerFactoryCustomizerTests { @Test void threadPoolWithMaxQueueCapacityEqualToZeroCustomizesThreadPool() { - bind("server.jetty.max-queue-capacity=0", "server.jetty.min-threads=100", "server.jetty.max-threads=100", - "server.jetty.thread-idle-timeout=6s"); + bind("server.jetty.threads.max-queue-capacity=0", "server.jetty.min-threads=100", + "server.jetty.max-threads=100", "server.jetty.threads.idle-timeout=6s"); JettyWebServer server = customizeAndGetServer(); QueuedThreadPool threadPool = (QueuedThreadPool) server.getServer().getThreadPool(); assertThat(threadPool.getMinThreads()).isEqualTo(100); @@ -208,7 +208,7 @@ class JettyWebServerFactoryCustomizerTests { @Test void threadPoolWithMaxQueueCapacityPositiveCreateBlockingArrayQueue() { - bind("server.jetty.max-queue-capacity=1234"); + bind("server.jetty.threads.max-queue-capacity=1234"); JettyWebServer server = customizeAndGetServer(); ThreadPool threadPool = server.getServer().getThreadPool(); BlockingQueue queue = getQueue(threadPool); @@ -219,8 +219,8 @@ class JettyWebServerFactoryCustomizerTests { @Test void threadPoolWithMaxQueueCapacityPositiveCustomizesThreadPool() { - bind("server.jetty.max-queue-capacity=1234", "server.jetty.min-threads=10", "server.jetty.max-threads=150", - "server.jetty.thread-idle-timeout=3s"); + bind("server.jetty.threads.max-queue-capacity=1234", "server.jetty.min-threads=10", + "server.jetty.max-threads=150", "server.jetty.threads.idle-timeout=3s"); JettyWebServer server = customizeAndGetServer(); QueuedThreadPool threadPool = (QueuedThreadPool) server.getServer().getThreadPool(); assertThat(threadPool.getMinThreads()).isEqualTo(10); diff --git a/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-jetty/src/main/resources/application.properties b/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-jetty/src/main/resources/application.properties index 4d7dbe1673..eab83fbdfd 100644 --- a/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-jetty/src/main/resources/application.properties +++ b/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-jetty/src/main/resources/application.properties @@ -1,3 +1,3 @@ server.compression.enabled: true server.compression.min-response-size: 1 -server.jetty.acceptors=2 +server.jetty.threads.acceptors=2