|
18 | 18 |
|
19 | 19 | import java.time.Duration;
|
20 | 20 | import java.util.List;
|
| 21 | +import java.util.concurrent.TimeUnit; |
21 | 22 | import java.util.function.ObjIntConsumer;
|
22 | 23 | import java.util.stream.Collectors;
|
23 | 24 |
|
| 25 | +import javax.management.ObjectName; |
| 26 | + |
24 | 27 | import org.apache.catalina.Lifecycle;
|
25 | 28 | import org.apache.catalina.valves.AccessLogValve;
|
26 | 29 | import org.apache.catalina.valves.ErrorReportValve;
|
|
30 | 33 | import org.apache.coyote.UpgradeProtocol;
|
31 | 34 | import org.apache.coyote.http11.AbstractHttp11Protocol;
|
32 | 35 | 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; |
33 | 39 |
|
34 | 40 | import org.springframework.boot.autoconfigure.web.ErrorProperties;
|
35 | 41 | import org.springframework.boot.autoconfigure.web.ErrorProperties.IncludeAttribute;
|
36 | 42 | import org.springframework.boot.autoconfigure.web.ServerProperties;
|
37 | 43 | import org.springframework.boot.autoconfigure.web.ServerProperties.Tomcat.Accesslog;
|
38 | 44 | import org.springframework.boot.autoconfigure.web.ServerProperties.Tomcat.Remoteip;
|
| 45 | +import org.springframework.boot.autoconfigure.web.ServerProperties.Tomcat.Threads; |
39 | 46 | import org.springframework.boot.cloud.CloudPlatform;
|
40 | 47 | import org.springframework.boot.context.properties.PropertyMapper;
|
41 | 48 | import org.springframework.boot.web.embedded.tomcat.ConfigurableTomcatWebServerFactory;
|
@@ -90,64 +97,76 @@ public void customize(ConfigurableTomcatWebServerFactory factory) {
|
90 | 97 | PropertyMapper map = PropertyMapper.get().alwaysApplyingWhenNonNull();
|
91 | 98 | map.from(properties::getBasedir).to(factory::setBaseDirectory);
|
92 | 99 | 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); |
96 | 103 | customizeRemoteIpValve(factory);
|
97 | 104 | 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); |
104 | 106 | 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)); |
108 | 110 | 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)); |
112 | 114 | map.from(properties::getMaxSwallowSize)
|
113 |
| - .asInt(DataSize::toBytes) |
114 |
| - .to((maxSwallowSize) -> customizeMaxSwallowSize(factory, maxSwallowSize)); |
| 115 | + .asInt(DataSize::toBytes) |
| 116 | + .to((maxSwallowSize) -> customizeMaxSwallowSize(factory, maxSwallowSize)); |
115 | 117 | 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)); |
119 | 121 | 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)); |
122 | 124 | map.from(properties::getUriEncoding).to(factory::setUriEncoding);
|
123 | 125 | map.from(properties::getConnectionTimeout)
|
124 |
| - .to((connectionTimeout) -> customizeConnectionTimeout(factory, connectionTimeout)); |
| 126 | + .to((connectionTimeout) -> customizeConnectionTimeout(factory, connectionTimeout)); |
125 | 127 | map.from(properties::getMaxConnections)
|
126 |
| - .when(this::isPositive) |
127 |
| - .to((maxConnections) -> customizeMaxConnections(factory, maxConnections)); |
| 128 | + .when(this::isPositive) |
| 129 | + .to((maxConnections) -> customizeMaxConnections(factory, maxConnections)); |
128 | 130 | map.from(properties::getAcceptCount)
|
129 |
| - .when(this::isPositive) |
130 |
| - .to((acceptCount) -> customizeAcceptCount(factory, acceptCount)); |
| 131 | + .when(this::isPositive) |
| 132 | + .to((acceptCount) -> customizeAcceptCount(factory, acceptCount)); |
131 | 133 | map.from(properties::getProcessorCache)
|
132 |
| - .to((processorCache) -> customizeProcessorCache(factory, processorCache)); |
| 134 | + .to((processorCache) -> customizeProcessorCache(factory, processorCache)); |
133 | 135 | map.from(properties::getKeepAliveTimeout)
|
134 |
| - .to((keepAliveTimeout) -> customizeKeepAliveTimeout(factory, keepAliveTimeout)); |
| 136 | + .to((keepAliveTimeout) -> customizeKeepAliveTimeout(factory, keepAliveTimeout)); |
135 | 137 | map.from(properties::getMaxKeepAliveRequests)
|
136 |
| - .to((maxKeepAliveRequests) -> customizeMaxKeepAliveRequests(factory, maxKeepAliveRequests)); |
| 138 | + .to((maxKeepAliveRequests) -> customizeMaxKeepAliveRequests(factory, maxKeepAliveRequests)); |
137 | 139 | 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)); |
141 | 143 | 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)); |
145 | 147 | map.from(properties::isRejectIllegalHeader)
|
146 |
| - .to((rejectIllegalHeader) -> customizeRejectIllegalHeader(factory, rejectIllegalHeader)); |
| 148 | + .to((rejectIllegalHeader) -> customizeRejectIllegalHeader(factory, rejectIllegalHeader)); |
147 | 149 | customizeStaticResources(factory);
|
148 | 150 | customizeErrorReportValve(this.serverProperties.getError(), factory);
|
149 | 151 | }
|
150 | 152 |
|
| 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 | + |
151 | 170 | private boolean isPositive(int value) {
|
152 | 171 | return value > 0;
|
153 | 172 | }
|
|
0 commit comments