Skip to content

Commit 636a3cd

Browse files
committed
Problems: - When setting an executor, Tomcat will no longer on shutdown stop it - Q: is there a way to only configure the TaskQueue on the existing handler?
1 parent ec9ac05 commit 636a3cd

File tree

3 files changed

+69
-38
lines changed

3 files changed

+69
-38
lines changed

spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/ServerProperties.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -905,6 +905,11 @@ public static class Threads {
905905
*/
906906
private int minSpare = 10;
907907

908+
/**
909+
* Maximum capacity of the thread pool's backing queue.
910+
*/
911+
private int maxQueueCapacity = Integer.MAX_VALUE;
912+
908913
public int getMax() {
909914
return this.max;
910915
}
@@ -921,6 +926,13 @@ public void setMinSpare(int minSpare) {
921926
this.minSpare = minSpare;
922927
}
923928

929+
public int getMaxQueueCapacity() {
930+
return this.maxQueueCapacity;
931+
}
932+
933+
public void setMaxQueueCapacity(int maxQueueCapacity) {
934+
this.maxQueueCapacity = maxQueueCapacity;
935+
}
924936
}
925937

926938
/**

spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/embedded/TomcatWebServerFactoryCustomizer.java

Lines changed: 56 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,12 @@
1818

1919
import java.time.Duration;
2020
import java.util.List;
21+
import java.util.concurrent.TimeUnit;
2122
import java.util.function.ObjIntConsumer;
2223
import java.util.stream.Collectors;
2324

25+
import javax.management.ObjectName;
26+
2427
import org.apache.catalina.Lifecycle;
2528
import org.apache.catalina.valves.AccessLogValve;
2629
import org.apache.catalina.valves.ErrorReportValve;
@@ -30,12 +33,16 @@
3033
import org.apache.coyote.UpgradeProtocol;
3134
import org.apache.coyote.http11.AbstractHttp11Protocol;
3235
import org.apache.coyote.http2.Http2Protocol;
36+
import org.apache.tomcat.util.threads.TaskQueue;
37+
import org.apache.tomcat.util.threads.TaskThreadFactory;
38+
import org.apache.tomcat.util.threads.ThreadPoolExecutor;
3339

3440
import org.springframework.boot.autoconfigure.web.ErrorProperties;
3541
import org.springframework.boot.autoconfigure.web.ErrorProperties.IncludeAttribute;
3642
import org.springframework.boot.autoconfigure.web.ServerProperties;
3743
import org.springframework.boot.autoconfigure.web.ServerProperties.Tomcat.Accesslog;
3844
import org.springframework.boot.autoconfigure.web.ServerProperties.Tomcat.Remoteip;
45+
import org.springframework.boot.autoconfigure.web.ServerProperties.Tomcat.Threads;
3946
import org.springframework.boot.cloud.CloudPlatform;
4047
import org.springframework.boot.context.properties.PropertyMapper;
4148
import org.springframework.boot.web.embedded.tomcat.ConfigurableTomcatWebServerFactory;
@@ -90,64 +97,76 @@ public void customize(ConfigurableTomcatWebServerFactory factory) {
9097
PropertyMapper map = PropertyMapper.get().alwaysApplyingWhenNonNull();
9198
map.from(properties::getBasedir).to(factory::setBaseDirectory);
9299
map.from(properties::getBackgroundProcessorDelay)
93-
.as(Duration::getSeconds)
94-
.as(Long::intValue)
95-
.to(factory::setBackgroundProcessorDelay);
100+
.as(Duration::getSeconds)
101+
.as(Long::intValue)
102+
.to(factory::setBackgroundProcessorDelay);
96103
customizeRemoteIpValve(factory);
97104
ServerProperties.Tomcat.Threads threadProperties = properties.getThreads();
98-
map.from(threadProperties::getMax)
99-
.when(this::isPositive)
100-
.to((maxThreads) -> customizeMaxThreads(factory, threadProperties.getMax()));
101-
map.from(threadProperties::getMinSpare)
102-
.when(this::isPositive)
103-
.to((minSpareThreads) -> customizeMinThreads(factory, minSpareThreads));
105+
configureQueue(factory, threadProperties);
104106
map.from(this.serverProperties.getMaxHttpRequestHeaderSize())
105-
.asInt(DataSize::toBytes)
106-
.when(this::isPositive)
107-
.to((maxHttpRequestHeaderSize) -> customizeMaxHttpRequestHeaderSize(factory, maxHttpRequestHeaderSize));
107+
.asInt(DataSize::toBytes)
108+
.when(this::isPositive)
109+
.to((maxHttpRequestHeaderSize) -> customizeMaxHttpRequestHeaderSize(factory, maxHttpRequestHeaderSize));
108110
map.from(properties::getMaxHttpResponseHeaderSize)
109-
.asInt(DataSize::toBytes)
110-
.when(this::isPositive)
111-
.to((maxHttpResponseHeaderSize) -> customizeMaxHttpResponseHeaderSize(factory, maxHttpResponseHeaderSize));
111+
.asInt(DataSize::toBytes)
112+
.when(this::isPositive)
113+
.to((maxHttpResponseHeaderSize) -> customizeMaxHttpResponseHeaderSize(factory, maxHttpResponseHeaderSize));
112114
map.from(properties::getMaxSwallowSize)
113-
.asInt(DataSize::toBytes)
114-
.to((maxSwallowSize) -> customizeMaxSwallowSize(factory, maxSwallowSize));
115+
.asInt(DataSize::toBytes)
116+
.to((maxSwallowSize) -> customizeMaxSwallowSize(factory, maxSwallowSize));
115117
map.from(properties::getMaxHttpFormPostSize)
116-
.asInt(DataSize::toBytes)
117-
.when((maxHttpFormPostSize) -> maxHttpFormPostSize != 0)
118-
.to((maxHttpFormPostSize) -> customizeMaxHttpFormPostSize(factory, maxHttpFormPostSize));
118+
.asInt(DataSize::toBytes)
119+
.when((maxHttpFormPostSize) -> maxHttpFormPostSize != 0)
120+
.to((maxHttpFormPostSize) -> customizeMaxHttpFormPostSize(factory, maxHttpFormPostSize));
119121
map.from(properties::getAccesslog)
120-
.when(ServerProperties.Tomcat.Accesslog::isEnabled)
121-
.to((enabled) -> customizeAccessLog(factory));
122+
.when(ServerProperties.Tomcat.Accesslog::isEnabled)
123+
.to((enabled) -> customizeAccessLog(factory));
122124
map.from(properties::getUriEncoding).to(factory::setUriEncoding);
123125
map.from(properties::getConnectionTimeout)
124-
.to((connectionTimeout) -> customizeConnectionTimeout(factory, connectionTimeout));
126+
.to((connectionTimeout) -> customizeConnectionTimeout(factory, connectionTimeout));
125127
map.from(properties::getMaxConnections)
126-
.when(this::isPositive)
127-
.to((maxConnections) -> customizeMaxConnections(factory, maxConnections));
128+
.when(this::isPositive)
129+
.to((maxConnections) -> customizeMaxConnections(factory, maxConnections));
128130
map.from(properties::getAcceptCount)
129-
.when(this::isPositive)
130-
.to((acceptCount) -> customizeAcceptCount(factory, acceptCount));
131+
.when(this::isPositive)
132+
.to((acceptCount) -> customizeAcceptCount(factory, acceptCount));
131133
map.from(properties::getProcessorCache)
132-
.to((processorCache) -> customizeProcessorCache(factory, processorCache));
134+
.to((processorCache) -> customizeProcessorCache(factory, processorCache));
133135
map.from(properties::getKeepAliveTimeout)
134-
.to((keepAliveTimeout) -> customizeKeepAliveTimeout(factory, keepAliveTimeout));
136+
.to((keepAliveTimeout) -> customizeKeepAliveTimeout(factory, keepAliveTimeout));
135137
map.from(properties::getMaxKeepAliveRequests)
136-
.to((maxKeepAliveRequests) -> customizeMaxKeepAliveRequests(factory, maxKeepAliveRequests));
138+
.to((maxKeepAliveRequests) -> customizeMaxKeepAliveRequests(factory, maxKeepAliveRequests));
137139
map.from(properties::getRelaxedPathChars)
138-
.as(this::joinCharacters)
139-
.whenHasText()
140-
.to((relaxedChars) -> customizeRelaxedPathChars(factory, relaxedChars));
140+
.as(this::joinCharacters)
141+
.whenHasText()
142+
.to((relaxedChars) -> customizeRelaxedPathChars(factory, relaxedChars));
141143
map.from(properties::getRelaxedQueryChars)
142-
.as(this::joinCharacters)
143-
.whenHasText()
144-
.to((relaxedChars) -> customizeRelaxedQueryChars(factory, relaxedChars));
144+
.as(this::joinCharacters)
145+
.whenHasText()
146+
.to((relaxedChars) -> customizeRelaxedQueryChars(factory, relaxedChars));
145147
map.from(properties::isRejectIllegalHeader)
146-
.to((rejectIllegalHeader) -> customizeRejectIllegalHeader(factory, rejectIllegalHeader));
148+
.to((rejectIllegalHeader) -> customizeRejectIllegalHeader(factory, rejectIllegalHeader));
147149
customizeStaticResources(factory);
148150
customizeErrorReportValve(this.serverProperties.getError(), factory);
149151
}
150152

153+
private void configureQueue(ConfigurableTomcatWebServerFactory factory, Threads threadProperties) {
154+
factory.addProtocolHandlerCustomizers((handler) -> {
155+
TaskQueue taskQueue = new TaskQueue(threadProperties.getMaxQueueCapacity());
156+
String name = "tomcat-handler-";
157+
int threadPriority = Thread.NORM_PRIORITY;
158+
if (handler instanceof AbstractProtocol<?> protocol) {
159+
name = ObjectName.unquote(protocol.getName()) + "-exec-";
160+
threadPriority = protocol.getThreadPriority();
161+
}
162+
ThreadPoolExecutor executor = new ThreadPoolExecutor(
163+
threadProperties.getMinSpare(), threadProperties.getMax(), 60, TimeUnit.SECONDS, taskQueue,
164+
new TaskThreadFactory(name, true, threadPriority));
165+
taskQueue.setParent(executor);
166+
handler.setExecutor(executor);
167+
});
168+
}
169+
151170
private boolean isPositive(int value) {
152171
return value > 0;
153172
}

spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-tomcat/src/main/java/smoketest/tomcat/SampleTomcatApplication.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ public void contextDestroyed(ServletContextEvent sce) {
4848
}
4949

5050
public static void main(String[] args) {
51-
SpringApplication.run(SampleTomcatApplication.class, args);
51+
SpringApplication.run(SampleTomcatApplication.class, args).close();
5252
}
5353

5454
}

0 commit comments

Comments
 (0)