Add property to limit maximum connections for Jetty

Closes gh-35899
This commit is contained in:
Moritz Halbritter 2023-06-06 09:39:33 +02:00
parent f81787e65d
commit 3e4a9f5204
7 changed files with 73 additions and 0 deletions

View File

@ -1123,6 +1123,12 @@ public class ServerProperties {
*/
private DataSize maxHttpResponseHeaderSize = DataSize.ofKilobytes(8);
/**
* Maximum number of connections that the server accepts and processes at any
* given time.
*/
private int maxConnections = -1;
public Accesslog getAccesslog() {
return this.accesslog;
}
@ -1155,6 +1161,14 @@ public class ServerProperties {
this.maxHttpResponseHeaderSize = maxHttpResponseHeaderSize;
}
public int getMaxConnections() {
return this.maxConnections;
}
public void setMaxConnections(int maxConnections) {
this.maxConnections = maxConnections;
}
/**
* Jetty access log properties.
*/

View File

@ -81,6 +81,7 @@ public class JettyWebServerFactoryCustomizer
ServerProperties.Jetty.Threads threadProperties = properties.getThreads();
factory.setThreadPool(determineThreadPool(properties.getThreads()));
PropertyMapper map = PropertyMapper.get().alwaysApplyingWhenNonNull();
map.from(properties::getMaxConnections).to(factory::setMaxConnections);
map.from(threadProperties::getAcceptors).to(factory::setAcceptors);
map.from(threadProperties::getSelectors).to(factory::setSelectors);
map.from(this.serverProperties::getMaxHttpRequestHeaderSize)

View File

@ -25,6 +25,7 @@ import org.springframework.boot.web.server.ConfigurableWebServerFactory;
* {@link ConfigurableWebServerFactory} for Jetty-specific features.
*
* @author Brian Clozel
* @author Moritz Halbritter
* @since 2.0.0
* @see JettyServletWebServerFactory
* @see JettyReactiveWebServerFactory
@ -63,4 +64,11 @@ public interface ConfigurableJettyWebServerFactory extends ConfigurableWebServer
*/
void addServerCustomizers(JettyServerCustomizer... customizers);
/**
* Sets the maximum number of concurrent connections.
* @param maxConnections the maximum number of concurrent connections
* @since 3.2.0
*/
void setMaxConnections(int maxConnections);
}

View File

@ -29,6 +29,7 @@ import org.apache.commons.logging.LogFactory;
import org.eclipse.jetty.http2.server.HTTP2CServerConnectionFactory;
import org.eclipse.jetty.server.AbstractConnector;
import org.eclipse.jetty.server.ConnectionFactory;
import org.eclipse.jetty.server.ConnectionLimit;
import org.eclipse.jetty.server.Handler;
import org.eclipse.jetty.server.HttpConfiguration;
import org.eclipse.jetty.server.HttpConnectionFactory;
@ -55,6 +56,7 @@ import org.springframework.util.StringUtils;
* {@link ReactiveWebServerFactory} that can be used to create {@link JettyWebServer}s.
*
* @author Brian Clozel
* @author Moritz Halbritter
* @since 2.0.0
*/
public class JettyReactiveWebServerFactory extends AbstractReactiveWebServerFactory
@ -80,6 +82,8 @@ public class JettyReactiveWebServerFactory extends AbstractReactiveWebServerFact
private ThreadPool threadPool;
private int maxConnections = -1;
/**
* Create a new {@link JettyServletWebServerFactory} instance.
*/
@ -118,6 +122,11 @@ public class JettyReactiveWebServerFactory extends AbstractReactiveWebServerFact
this.jettyServerCustomizers.addAll(Arrays.asList(customizers));
}
@Override
public void setMaxConnections(int maxConnections) {
this.maxConnections = maxConnections;
}
/**
* Sets {@link JettyServerCustomizer}s that will be applied to the {@link Server}
* before it is started. Calling this method will replace any existing customizers.
@ -180,6 +189,9 @@ public class JettyReactiveWebServerFactory extends AbstractReactiveWebServerFact
contextHandler.addServlet(servletHolder, "/");
server.setHandler(addHandlerWrappers(contextHandler));
JettyReactiveWebServerFactory.logger.info("Server initialized with port: " + port);
if (this.maxConnections > -1) {
server.addBean(new ConnectionLimit(this.maxConnections, server));
}
if (Ssl.isEnabled(getSsl())) {
customizeSsl(server, address);
}

View File

@ -43,6 +43,7 @@ import org.eclipse.jetty.http.MimeTypes;
import org.eclipse.jetty.http2.server.HTTP2CServerConnectionFactory;
import org.eclipse.jetty.server.AbstractConnector;
import org.eclipse.jetty.server.ConnectionFactory;
import org.eclipse.jetty.server.ConnectionLimit;
import org.eclipse.jetty.server.Connector;
import org.eclipse.jetty.server.Handler;
import org.eclipse.jetty.server.HttpConfiguration;
@ -101,6 +102,7 @@ import org.springframework.util.StringUtils;
* @author Eddú Meléndez
* @author Venil Noronha
* @author Henri Kerola
* @author Moritz Halbritter
* @since 2.0.0
* @see #setPort(int)
* @see #setConfigurations(Collection)
@ -129,6 +131,8 @@ public class JettyServletWebServerFactory extends AbstractServletWebServerFactor
private ThreadPool threadPool;
private int maxConnections = -1;
/**
* Create a new {@link JettyServletWebServerFactory} instance.
*/
@ -163,6 +167,9 @@ public class JettyServletWebServerFactory extends AbstractServletWebServerFactor
configureWebAppContext(context, initializers);
server.setHandler(addHandlerWrappers(context));
this.logger.info("Server initialized with port: " + port);
if (this.maxConnections > -1) {
server.addBean(new ConnectionLimit(this.maxConnections, server));
}
if (Ssl.isEnabled(getSsl())) {
customizeSsl(server, address);
}
@ -458,6 +465,11 @@ public class JettyServletWebServerFactory extends AbstractServletWebServerFactor
this.selectors = selectors;
}
@Override
public void setMaxConnections(int maxConnections) {
this.maxConnections = maxConnections;
}
/**
* Sets {@link JettyServerCustomizer}s that will be applied to the {@link Server}
* before it is started. Calling this method will replace any existing customizers.

View File

@ -22,6 +22,7 @@ import java.time.Duration;
import java.util.Arrays;
import org.awaitility.Awaitility;
import org.eclipse.jetty.server.ConnectionLimit;
import org.eclipse.jetty.server.Connector;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.ServerConnector;
@ -47,6 +48,7 @@ import static org.mockito.Mockito.mock;
*
* @author Brian Clozel
* @author Madhura Bhave
* @author Moritz Halbritter
*/
@Servlet5ClassPathOverrides
class JettyReactiveWebServerFactoryTests extends AbstractReactiveWebServerFactoryTests {
@ -148,4 +150,15 @@ class JettyReactiveWebServerFactoryTests extends AbstractReactiveWebServerFactor
this.webServer.stop();
}
@Test
void shouldApplyMaxConnections() {
JettyReactiveWebServerFactory factory = getFactory();
factory.setMaxConnections(1);
this.webServer = factory.getWebServer(new EchoHandler());
Server server = ((JettyWebServer) this.webServer).getServer();
ConnectionLimit connectionLimit = server.getBean(ConnectionLimit.class);
assertThat(connectionLimit).isNotNull();
assertThat(connectionLimit.getMaxConnections()).isOne();
}
}

View File

@ -40,6 +40,7 @@ import org.apache.hc.core5.http.Header;
import org.apache.hc.core5.http.HttpResponse;
import org.apache.jasper.servlet.JspServlet;
import org.awaitility.Awaitility;
import org.eclipse.jetty.server.ConnectionLimit;
import org.eclipse.jetty.server.Connector;
import org.eclipse.jetty.server.Handler;
import org.eclipse.jetty.server.Server;
@ -86,6 +87,7 @@ import static org.mockito.Mockito.mock;
* @author Dave Syer
* @author Andy Wilkinson
* @author Henri Kerola
* @author Moritz Halbritter
*/
@Servlet5ClassPathOverrides
class JettyServletWebServerFactoryTests extends AbstractServletWebServerFactoryTests {
@ -518,6 +520,17 @@ class JettyServletWebServerFactoryTests extends AbstractServletWebServerFactoryT
assertThat(context.getErrorHandler()).isInstanceOf(CustomErrorHandler.class);
}
@Test
void shouldApplyMaxConnections() {
JettyServletWebServerFactory factory = getFactory();
factory.setMaxConnections(1);
this.webServer = factory.getWebServer();
Server server = ((JettyWebServer) this.webServer).getServer();
ConnectionLimit connectionLimit = server.getBean(ConnectionLimit.class);
assertThat(connectionLimit).isNotNull();
assertThat(connectionLimit.getMaxConnections()).isOne();
}
private WebAppContext findWebAppContext(JettyWebServer webServer) {
return findWebAppContext(webServer.getServer().getHandler());
}