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..919ab9878e8 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 amount of threads. + */ + private Integer maxThreads = 200; + + /** + * Minimum amount 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..8b488e456f1 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 @@ -29,6 +29,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 +48,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 +81,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) -> customizeMaxThreads(factory, maxThreads)); + propertyMapper.from(jettyProperties::getMinThreads).when(this::isPositive) + .to((minThreads) -> customizeMinThreads(factory, minThreads)); + propertyMapper.from(jettyProperties::getIdleTimeout).when(this::isPositive) + .to((idleTimeout) -> customizeIdleTimeout(factory, idleTimeout)); propertyMapper.from(properties::getConnectionTimeout).whenNonNull() .to((connectionTimeout) -> customizeConnectionTimeout(factory, connectionTimeout)); propertyMapper.from(jettyProperties::getAccesslog).when(ServerProperties.Jetty.Accesslog::isEnabled) @@ -131,6 +140,33 @@ public class JettyWebServerFactoryCustomizer }); } + private void customizeMaxThreads(ConfigurableJettyWebServerFactory factory, int maxThreads) { + factory.addServerCustomizers((connector) -> { + ThreadPool threadPool = connector.getThreadPool(); + if (threadPool instanceof QueuedThreadPool) { + ((QueuedThreadPool) threadPool).setMaxThreads(maxThreads); + } + }); + } + + private void customizeMinThreads(ConfigurableJettyWebServerFactory factory, int minThreads) { + factory.addServerCustomizers((connector) -> { + ThreadPool threadPool = connector.getThreadPool(); + if (threadPool instanceof QueuedThreadPool) { + ((QueuedThreadPool) threadPool).setMinThreads(minThreads); + } + }); + } + + private void customizeIdleTimeout(ConfigurableJettyWebServerFactory factory, int idleTimeout) { + factory.addServerCustomizers((connector) -> { + ThreadPool threadPool = connector.getThreadPool(); + if (threadPool instanceof QueuedThreadPool) { + ((QueuedThreadPool) threadPool).setIdleTimeout(idleTimeout); + } + }); + } + 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..1e423c6a98c 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,8 @@ 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.eclipse.jetty.util.thread.ThreadPool; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -49,6 +51,7 @@ import static org.mockito.Mockito.verify; * * @author Brian Clozel * @author Phillip Webb + * @author HaiTao Zhang */ class JettyWebServerFactoryCustomizerTests { @@ -112,6 +115,36 @@ class JettyWebServerFactoryCustomizerTests { assertThat(logWriter.isAppend()).isFalse(); } + @Test + void maxThreadsCanBeCustomized() throws IOException { + bind("server.jetty.max-threads=100"); + JettyWebServer server = customizeAndGetServer(); + ThreadPool threadPool = server.getServer().getThreadPool(); + if (threadPool instanceof QueuedThreadPool) { + assertThat(((QueuedThreadPool) threadPool).getMaxThreads()).isEqualTo(100); + } + } + + @Test + void minThreadsCanBeCustomized() throws IOException { + bind("server.jetty.min-threads=100"); + JettyWebServer server = customizeAndGetServer(); + ThreadPool threadPool = server.getServer().getThreadPool(); + if (threadPool instanceof QueuedThreadPool) { + assertThat(((QueuedThreadPool) threadPool).getMinThreads()).isEqualTo(100); + } + } + + @Test + void idleTimeoutCanBeCustomized() throws IOException { + bind("server.jetty.idle-timeout=100"); + JettyWebServer server = customizeAndGetServer(); + ThreadPool threadPool = server.getServer().getThreadPool(); + if (threadPool instanceof QueuedThreadPool) { + assertThat(((QueuedThreadPool) threadPool).getIdleTimeout()).isEqualTo(100); + } + } + private CustomRequestLog getRequestLog(JettyWebServer server) { RequestLog requestLog = server.getServer().getRequestLog(); assertThat(requestLog).isInstanceOf(CustomRequestLog.class);