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 7c359abb4cd..0e2eb8daf96 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 @@ -62,6 +62,7 @@ import org.springframework.util.unit.DataSize; * @author Andrew McGhie * @author Rafiullah Hamedy * @author Dirk Deyne + * @author HaiTao Zhang * @since 1.0.0 */ @ConfigurationProperties(prefix = "server", ignoreUnknownFields = true) @@ -905,6 +906,21 @@ public class ServerProperties { */ private Integer selectors = -1; + /** + * Maximum number of threads. + */ + private Integer maxThreads = 200; + + /** + * Minimum number of threads. + */ + private Integer minThreads = 8; + + /** + * Maximum thread idle time. + */ + private Integer idleTimeout = 60000; + public Accesslog getAccesslog() { return this.accesslog; } @@ -933,6 +949,30 @@ public class ServerProperties { this.selectors = selectors; } + public void setMinThreads(Integer minThreads) { + this.minThreads = minThreads; + } + + public Integer getMinThreads() { + return this.minThreads; + } + + public void setMaxThreads(Integer maxThreads) { + this.maxThreads = maxThreads; + } + + public Integer getMaxThreads() { + return this.maxThreads; + } + + public void setIdleTimeout(Integer idleTimeout) { + this.idleTimeout = idleTimeout; + } + + public Integer getIdleTimeout() { + return this.idleTimeout; + } + /** * Jetty access log properties. */ 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 9fd9dd0e92e..fab6ae591dc 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 @@ -18,6 +18,7 @@ package org.springframework.boot.autoconfigure.web.embedded; import java.time.Duration; import java.util.Arrays; +import java.util.function.Consumer; import org.eclipse.jetty.server.AbstractConnector; import org.eclipse.jetty.server.ConnectionFactory; @@ -29,6 +30,8 @@ import org.eclipse.jetty.server.Server; import org.eclipse.jetty.server.handler.ContextHandler; import org.eclipse.jetty.server.handler.HandlerCollection; import org.eclipse.jetty.server.handler.HandlerWrapper; +import org.eclipse.jetty.util.thread.QueuedThreadPool; +import org.eclipse.jetty.util.thread.ThreadPool; import org.springframework.boot.autoconfigure.web.ServerProperties; import org.springframework.boot.cloud.CloudPlatform; @@ -46,6 +49,7 @@ import org.springframework.util.unit.DataSize; * * @author Brian Clozel * @author Phillip Webb + * @author HaiTao Zhang * @since 2.0.0 */ public class JettyWebServerFactoryCustomizer @@ -78,6 +82,12 @@ public class JettyWebServerFactoryCustomizer .addServerCustomizers(new MaxHttpHeaderSizeCustomizer(maxHttpHeaderSize))); propertyMapper.from(jettyProperties::getMaxHttpPostSize).asInt(DataSize::toBytes).when(this::isPositive) .to((maxHttpPostSize) -> customizeMaxHttpPostSize(factory, maxHttpPostSize)); + propertyMapper.from(jettyProperties::getMaxThreads).when(this::isPositive) + .to((maxThreads) -> customizeThreadPool(factory, (threadPool) -> threadPool.setMaxThreads(maxThreads))); + propertyMapper.from(jettyProperties::getMinThreads).when(this::isPositive) + .to((minThreads) -> customizeThreadPool(factory, (threadPool) -> threadPool.setMinThreads(minThreads))); + propertyMapper.from(jettyProperties::getIdleTimeout).when(this::isPositive).to( + (idleTimeout) -> customizeThreadPool(factory, (threadPool) -> threadPool.setIdleTimeout(idleTimeout))); propertyMapper.from(properties::getConnectionTimeout).whenNonNull() .to((connectionTimeout) -> customizeConnectionTimeout(factory, connectionTimeout)); propertyMapper.from(jettyProperties::getAccesslog).when(ServerProperties.Jetty.Accesslog::isEnabled) @@ -131,6 +141,15 @@ public class JettyWebServerFactoryCustomizer }); } + private void customizeThreadPool(ConfigurableJettyWebServerFactory factory, Consumer customizer) { + factory.addServerCustomizers((connector) -> { + ThreadPool threadPool = connector.getThreadPool(); + if (threadPool instanceof QueuedThreadPool) { + customizer.accept((QueuedThreadPool) threadPool); + } + }); + } + private void customizeAccessLog(ConfigurableJettyWebServerFactory factory, ServerProperties.Jetty.Accesslog properties) { factory.addServerCustomizers((server) -> { 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 ea6d2b5362e..bebde8228c2 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 @@ -74,6 +74,7 @@ import static org.assertj.core.api.Assertions.assertThat; * @author Quinten De Swaef * @author Venil Noronha * @author Andrew McGhie + * @author HaiTao Zhang */ class ServerPropertiesTests { @@ -218,6 +219,24 @@ class ServerPropertiesTests { assertThat(this.properties.getJetty().getSelectors()).isEqualTo(10); } + @Test + void testCustomizeJettyMaxThreads() { + bind("server.jetty.max-threads", "10"); + assertThat(this.properties.getJetty().getMaxThreads()).isEqualTo(10); + } + + @Test + void testCustomizeJettyMinThreads() { + bind("server.jetty.min-threads", "10"); + assertThat(this.properties.getJetty().getMinThreads()).isEqualTo(10); + } + + @Test + void testCustomizeJettyIdleTimeout() { + bind("server.jetty.idle-timeout", "10"); + assertThat(this.properties.getJetty().getIdleTimeout()).isEqualTo(10); + } + @Test void testCustomizeUndertowServerOption() { bind("server.undertow.options.server.ALWAYS_SET_KEEP_ALIVE", "true"); 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 91373301c22..324bfad4a5b 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 @@ -27,6 +27,7 @@ import org.eclipse.jetty.server.HttpConfiguration; import org.eclipse.jetty.server.HttpConfiguration.ConnectionFactory; import org.eclipse.jetty.server.RequestLog; import org.eclipse.jetty.server.RequestLogWriter; +import org.eclipse.jetty.util.thread.QueuedThreadPool; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -49,6 +50,7 @@ import static org.mockito.Mockito.verify; * * @author Brian Clozel * @author Phillip Webb + * @author HaiTao Zhang */ class JettyWebServerFactoryCustomizerTests { @@ -112,6 +114,30 @@ class JettyWebServerFactoryCustomizerTests { assertThat(logWriter.isAppend()).isFalse(); } + @Test + void maxThreadsCanBeCustomized() { + bind("server.jetty.max-threads=100"); + JettyWebServer server = customizeAndGetServer(); + QueuedThreadPool threadPool = (QueuedThreadPool) server.getServer().getThreadPool(); + assertThat(threadPool.getMaxThreads()).isEqualTo(100); + } + + @Test + void minThreadsCanBeCustomized() { + bind("server.jetty.min-threads=100"); + JettyWebServer server = customizeAndGetServer(); + QueuedThreadPool threadPool = (QueuedThreadPool) server.getServer().getThreadPool(); + assertThat(threadPool.getMinThreads()).isEqualTo(100); + } + + @Test + void idleTimeoutCanBeCustomized() { + bind("server.jetty.idle-timeout=100"); + JettyWebServer server = customizeAndGetServer(); + QueuedThreadPool threadPool = (QueuedThreadPool) server.getServer().getThreadPool(); + assertThat(threadPool.getIdleTimeout()).isEqualTo(100); + } + private CustomRequestLog getRequestLog(JettyWebServer server) { RequestLog requestLog = server.getServer().getRequestLog(); assertThat(requestLog).isInstanceOf(CustomRequestLog.class);