Polish "Add support for configuring Jetty's backing queue"
See gh-19494pull/20193/head
parent
852734b129
commit
b56c4f1a4d
@ -1,102 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2012-2019 the original author or authors.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* https://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.springframework.boot.autoconfigure.web.embedded;
|
|
||||||
|
|
||||||
import java.time.Duration;
|
|
||||||
import java.util.concurrent.BlockingQueue;
|
|
||||||
import java.util.concurrent.SynchronousQueue;
|
|
||||||
|
|
||||||
import org.eclipse.jetty.util.BlockingArrayQueue;
|
|
||||||
import org.eclipse.jetty.util.thread.QueuedThreadPool;
|
|
||||||
|
|
||||||
import org.springframework.boot.autoconfigure.web.ServerProperties;
|
|
||||||
import org.springframework.boot.autoconfigure.web.ServerProperties.Jetty;
|
|
||||||
import org.springframework.boot.web.embedded.jetty.JettyThreadPoolFactory;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A {@link JettyThreadPoolFactory} that creates a thread pool that uses a backing queue
|
|
||||||
* with a max capacity if the {@link Jetty#maxQueueCapacity} is specified. If the max
|
|
||||||
* capacity is not specified then the factory will return null thus allowing the standard
|
|
||||||
* Jetty server thread pool to be used.
|
|
||||||
*
|
|
||||||
* @author Chris Bono
|
|
||||||
* @since 2.3.0
|
|
||||||
*/
|
|
||||||
public class JettyConstrainedQueuedThreadPoolFactory implements JettyThreadPoolFactory<QueuedThreadPool> {
|
|
||||||
|
|
||||||
private ServerProperties serverProperties;
|
|
||||||
|
|
||||||
public JettyConstrainedQueuedThreadPoolFactory(ServerProperties serverProperties) {
|
|
||||||
this.serverProperties = serverProperties;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* <p>
|
|
||||||
* Creates a {@link QueuedThreadPool} with the following settings (if
|
|
||||||
* {@link Jetty#maxQueueCapacity} is specified):
|
|
||||||
* <ul>
|
|
||||||
* <li>min threads set to {@link Jetty#minThreads} or {@code 8} if not specified.
|
|
||||||
* <li>max threads set to {@link Jetty#maxThreads} or {@code 200} if not specified.
|
|
||||||
* <li>idle timeout set to {@link Jetty#threadIdleTimeout} or {@code 60000} if not
|
|
||||||
* specified.</li>
|
|
||||||
* <li>if {@link Jetty#maxQueueCapacity} is zero its backing queue will be a
|
|
||||||
* {@link SynchronousQueue} otherwise it will be a {@link BlockingArrayQueue} whose
|
|
||||||
* max capacity is set to the max queue depth.
|
|
||||||
* </ul>
|
|
||||||
* @return thread pool as described above or {@code null} if
|
|
||||||
* {@link Jetty#maxQueueCapacity} is not specified.
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public QueuedThreadPool create() {
|
|
||||||
|
|
||||||
Integer maxQueueCapacity = this.serverProperties.getJetty().getMaxQueueCapacity();
|
|
||||||
|
|
||||||
// Max depth is not specified - let Jetty server use its defaults in this case
|
|
||||||
if (maxQueueCapacity == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
BlockingQueue<Runnable> queue;
|
|
||||||
if (maxQueueCapacity == 0) {
|
|
||||||
/**
|
|
||||||
* This queue will cause jetty to reject requests whenever there is no idle
|
|
||||||
* thread available to handle them. If this queue is used, it is strongly
|
|
||||||
* recommended to set _minThreads equal to _maxThreads. Jetty's
|
|
||||||
* QueuedThreadPool class may not behave like a regular java thread pool and
|
|
||||||
* may not add threads properly when a SynchronousQueue is used.
|
|
||||||
*/
|
|
||||||
queue = new SynchronousQueue<>();
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
/**
|
|
||||||
* Create a queue of fixed size. This queue will not grow. If a request
|
|
||||||
* arrives and the queue is empty, the client will see an immediate
|
|
||||||
* "connection reset" error.
|
|
||||||
*/
|
|
||||||
queue = new BlockingArrayQueue<>(maxQueueCapacity);
|
|
||||||
}
|
|
||||||
|
|
||||||
Integer maxThreadCount = this.serverProperties.getJetty().getMaxThreads();
|
|
||||||
Integer minThreadCount = this.serverProperties.getJetty().getMinThreads();
|
|
||||||
Duration threadIdleTimeout = this.serverProperties.getJetty().getThreadIdleTimeout();
|
|
||||||
|
|
||||||
return new QueuedThreadPool((maxThreadCount != null) ? maxThreadCount : 200,
|
|
||||||
(minThreadCount != null) ? minThreadCount : 8,
|
|
||||||
(threadIdleTimeout != null) ? (int) threadIdleTimeout.toMillis() : 60000, queue);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,108 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2012-2019 the original author or authors.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* https://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.springframework.boot.autoconfigure.web.embedded;
|
|
||||||
|
|
||||||
import java.lang.reflect.Method;
|
|
||||||
import java.util.concurrent.BlockingQueue;
|
|
||||||
import java.util.concurrent.SynchronousQueue;
|
|
||||||
|
|
||||||
import org.eclipse.jetty.util.BlockingArrayQueue;
|
|
||||||
import org.eclipse.jetty.util.thread.QueuedThreadPool;
|
|
||||||
import org.junit.jupiter.api.BeforeEach;
|
|
||||||
import org.junit.jupiter.api.Test;
|
|
||||||
|
|
||||||
import org.springframework.boot.autoconfigure.web.ServerProperties;
|
|
||||||
import org.springframework.boot.context.properties.bind.Bindable;
|
|
||||||
import org.springframework.boot.context.properties.bind.Binder;
|
|
||||||
import org.springframework.boot.context.properties.source.ConfigurationPropertySources;
|
|
||||||
import org.springframework.mock.env.MockEnvironment;
|
|
||||||
import org.springframework.test.context.support.TestPropertySourceUtils;
|
|
||||||
import org.springframework.util.ReflectionUtils;
|
|
||||||
|
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Tests for {@link JettyConstrainedQueuedThreadPoolFactory}.
|
|
||||||
*
|
|
||||||
* @author Chris Bono
|
|
||||||
*/
|
|
||||||
class JettyConstrainedQueuedThreadPoolFactoryTest {
|
|
||||||
|
|
||||||
private MockEnvironment environment;
|
|
||||||
|
|
||||||
private ServerProperties serverProperties;
|
|
||||||
|
|
||||||
private JettyConstrainedQueuedThreadPoolFactory factory;
|
|
||||||
|
|
||||||
@BeforeEach
|
|
||||||
void setup() {
|
|
||||||
this.environment = new MockEnvironment();
|
|
||||||
this.serverProperties = new ServerProperties();
|
|
||||||
ConfigurationPropertySources.attach(this.environment);
|
|
||||||
this.factory = new JettyConstrainedQueuedThreadPoolFactory(this.serverProperties);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void factoryReturnsNullWhenMaxCapacityNotSpecified() {
|
|
||||||
bind("server.jetty.max-queue-capacity=");
|
|
||||||
assertThat(this.factory.create()).isNull();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void factoryReturnsSynchronousQueueWhenMaxCapacityIsZero() {
|
|
||||||
bind("server.jetty.max-queue-capacity=0");
|
|
||||||
QueuedThreadPool queuedThreadPool = this.factory.create();
|
|
||||||
assertThat(getQueue(queuedThreadPool, SynchronousQueue.class)).isNotNull();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void factoryReturnsBlockingArrayQueueWithDefaultsWhenOnlyMaxCapacityIsSet() {
|
|
||||||
bind("server.jetty.max-queue-capacity=5150");
|
|
||||||
QueuedThreadPool queuedThreadPool = this.factory.create();
|
|
||||||
assertThat(queuedThreadPool.getMinThreads()).isEqualTo(8);
|
|
||||||
assertThat(queuedThreadPool.getMaxThreads()).isEqualTo(200);
|
|
||||||
assertThat(queuedThreadPool.getIdleTimeout()).isEqualTo(60000);
|
|
||||||
assertThat(getQueue(queuedThreadPool, BlockingArrayQueue.class).getMaxCapacity()).isEqualTo(5150);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void factoryReturnsBlockingArrayQueueWithCustomValues() {
|
|
||||||
bind("server.jetty.max-queue-capacity=5150", "server.jetty.min-threads=200", "server.jetty.max-threads=1000",
|
|
||||||
"server.jetty.thread-idle-timeout=10000");
|
|
||||||
QueuedThreadPool queuedThreadPool = this.factory.create();
|
|
||||||
assertThat(queuedThreadPool.getMinThreads()).isEqualTo(200);
|
|
||||||
assertThat(queuedThreadPool.getMaxThreads()).isEqualTo(1000);
|
|
||||||
assertThat(queuedThreadPool.getIdleTimeout()).isEqualTo(10000);
|
|
||||||
assertThat(getQueue(queuedThreadPool, BlockingArrayQueue.class).getMaxCapacity()).isEqualTo(5150);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void bind(String... inlinedProperties) {
|
|
||||||
TestPropertySourceUtils.addInlinedPropertiesToEnvironment(this.environment, inlinedProperties);
|
|
||||||
new Binder(ConfigurationPropertySources.get(this.environment)).bind("server",
|
|
||||||
Bindable.ofInstance(this.serverProperties));
|
|
||||||
}
|
|
||||||
|
|
||||||
static <T extends BlockingQueue<Runnable>> T getQueue(QueuedThreadPool queuedThreadPool,
|
|
||||||
Class<T> expectedQueueClass) {
|
|
||||||
Method getQueue = ReflectionUtils.findMethod(QueuedThreadPool.class, "getQueue");
|
|
||||||
ReflectionUtils.makeAccessible(getQueue);
|
|
||||||
Object obj = ReflectionUtils.invokeMethod(getQueue, queuedThreadPool);
|
|
||||||
assertThat(obj).isInstanceOf(expectedQueueClass);
|
|
||||||
return expectedQueueClass.cast(obj);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,39 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2012-2019 the original author or authors.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* https://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.springframework.boot.web.embedded.jetty;
|
|
||||||
|
|
||||||
import org.eclipse.jetty.server.Server;
|
|
||||||
import org.eclipse.jetty.util.thread.ThreadPool;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Factory to create a thread pool for use by a Jetty {@link Server}.
|
|
||||||
*
|
|
||||||
* @author Chris Bono
|
|
||||||
* @since 2.3.0
|
|
||||||
* @param <T> type of ThreadPool that the factory returns
|
|
||||||
*/
|
|
||||||
@FunctionalInterface
|
|
||||||
public interface JettyThreadPoolFactory<T extends ThreadPool> {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a thread pool.
|
|
||||||
* @return a Jetty thread pool or null to use the default Jetty {@link Server} thread
|
|
||||||
* pool.
|
|
||||||
*/
|
|
||||||
T create();
|
|
||||||
|
|
||||||
}
|
|
Loading…
Reference in New Issue