Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixes #7515 - Connection limit problem for "onAccepting" connections. #12320

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,6 @@
import org.eclipse.jetty.client.HttpClient;
import org.eclipse.jetty.ee10.servlet.DefaultServlet;
import org.eclipse.jetty.ee10.servlet.ResourceServlet;
import org.eclipse.jetty.ee10.servlet.ResourceServlet;
import org.eclipse.jetty.ee10.servlet.ServletContextHandler;
import org.eclipse.jetty.ee10.servlet.ServletHolder;
import org.eclipse.jetty.ee10.webapp.WebAppContext;
Expand All @@ -61,6 +60,7 @@
import org.eclipse.jetty.rewrite.handler.RewriteHandler;
import org.eclipse.jetty.rewrite.handler.RewriteRegexRule;
import org.eclipse.jetty.server.ConnectionFactory;
import org.eclipse.jetty.server.ConnectionLimit;
import org.eclipse.jetty.server.Connector;
import org.eclipse.jetty.server.CustomRequestLog;
import org.eclipse.jetty.server.FormFields;
Expand Down Expand Up @@ -353,6 +353,28 @@ public void onOpen(NetworkConnector connector)
// end::sameRandomPort[]
}

public void connectionLimit()
{
// tag::connectionLimit[]
Server server = new Server();

// Limit connections to the server, across all connectors.
ConnectionLimit serverConnectionLimit = new ConnectionLimit(1024, server);
server.addBean(serverConnectionLimit);

ServerConnector connector1 = new ServerConnector(server);
connector1.setPort(8080);
server.addConnector(connector1);

ServerConnector connector2 = new ServerConnector(server);
connector2.setPort(9090);
server.addConnector(connector2);
// Limit connections for this connector only.
ConnectionLimit connectorConnectionLimit = new ConnectionLimit(64, connector2);
connector2.addBean(connectorConnectionLimit);
// end::connectionLimit[]
}

public void sslHandshakeListener() throws Exception
{
// tag::sslHandshakeListener[]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,22 @@ This property allows you to cap the max heap memory retained by the pool.
`jetty.byteBufferPool.maxDirectMemory`::
This property allows you to cap the max direct memory retained by the pool.

[[connectionlimit]]
== Module `connectionlimit`

The `connectionlimit` module limits the number of connections accepted by the server, across all connectors.

Once the configured maximum number of connections is reached, Jetty will not accept more connections.
Existing, established connections will work normally.
When existing connections are closed, accepting new connections will be resumed.

NOTE: The number of connections seen at the JVM level may be different from the number of connections seen at the OS level.
For more information, refer to xref:programming-guide:server/http.adoc#connector-limiting[this section].

The module file is `$JETTY_HOME/modules/connectionlimit.mod`:

include::{jetty-home}/modules/connectionlimit.mod[tags=documentation]

[[console-capture]]
== Module `console-capture`

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -394,6 +394,27 @@ For example:
include::code:example$src/main/java/org/eclipse/jetty/docs/programming/server/http/HTTPServerDocs.java[tags=sameRandomPort]
----

[[connector-limiting]]
=== Limiting Connections

It is possible to limit the number of connections accepted by the whole server (and therefore across all connectors), or by a specific connector.

This feature is implemented by class `org.eclipse.jetty.server.ConnectionLimit` and you can use it in this way:

[,java,indent=0,options=nowrap]
----
include::code:example$src/main/java/org/eclipse/jetty/docs/programming/server/http/HTTPServerDocs.java[tags=connectionLimit]
----

[NOTE]
====
When the maximum number of connections is reached, no more connections will be accepted _at the JVM level_ -- but they could be accepted at the OS level.

This means that if you are using OS tools (like Linux's `ss`) to count the number of established connections, you may find a number that may be greater than the maximum number of connections configured in a `ConnectionLimit`.

Note also that different operative systems may behave differently when Jetty is not accepting connections: some OS accepts connections at the TCP level anyway (but does not notify this event to the JVM), some other OS may not accept connections at the TCP level.
====

[[connector-protocol]]
=== Configuring Protocols

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
import java.net.ConnectException;
import java.net.SocketTimeoutException;
import java.nio.channels.CancelledKeyException;
import java.nio.channels.ClosedChannelException;
import java.nio.channels.ClosedSelectorException;
import java.nio.channels.SelectableChannel;
import java.nio.channels.SelectionKey;
Expand Down Expand Up @@ -864,14 +865,6 @@ class Accept implements SelectorUpdate, Runnable, Closeable
_selectorManager.onAccepting(channel);
}

@Override
public void close()
{
if (LOG.isDebugEnabled())
LOG.debug("closed accept of {}", channel);
IO.close(channel);
}

@Override
public void update(Selector selector)
{
Expand All @@ -882,10 +875,9 @@ public void update(Selector selector)
}
catch (Throwable x)
{
IO.close(channel);
_selectorManager.onAcceptFailed(channel, x);
if (LOG.isDebugEnabled())
LOG.debug("Could not register channel after accept {}", channel, x);
failed(x);
}
}

Expand All @@ -894,22 +886,28 @@ public void run()
{
try
{
createEndPoint(channel, key);
_selectorManager.onAccepted(channel);
createEndPoint(channel, key);
}
catch (Throwable x)
{
if (LOG.isDebugEnabled())
LOG.debug("Could not process accepted channel {}", channel, x);
failed(x);
}
}

protected void failed(Throwable failure)
@Override
public void close()
{
IO.close(channel);
if (LOG.isDebugEnabled())
LOG.warn("Could not accept {}", channel, failure);
else
LOG.warn("Could not accept {}: {}", channel, String.valueOf(failure));
LOG.debug("Closed accept of {}", channel);
failed(new ClosedChannelException());
}

private void failed(Throwable failure)
{
IO.close(channel);
_selectorManager.onAcceptFailed(channel, failure);
}

Expand Down Expand Up @@ -1028,6 +1026,8 @@ public void update(Selector selector)
IO.close((Closeable)attachment);
}
_selector = null;
if (LOG.isDebugEnabled())
LOG.debug("Closing {} on {}", selector, ManagedSelector.this);
IO.close(selector);
}
finally
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# DO NOT EDIT THIS FILE - See: https://jetty.org/docs/

[description]
Enables a server-wide connection limit.
Enables a server-wide limit on TCP connections.

[tags]
connector
Expand All @@ -13,9 +13,10 @@ server
etc/jetty-connectionlimit.xml

[ini-template]

## The limit of connections to apply
#tag::documentation[]
## The maximum number of TCP connections allowed across all connectors.
#jetty.connectionlimit.maxConnections=1000

## The idle timeout to apply (in milliseconds) when connections are limited
## The idle timeout to apply (in milliseconds) to existing connections when the connection limit is reached.
#jetty.connectionlimit.idleTimeout=1000
#end::documentation[]
Loading
Loading