Skip to content

Commit 67e2b4a

Browse files
authored
Fixes #5306 - Default jetty.*.acceptors should be 1. (#6236)
* Fixes #5306 - Default jetty.*.acceptors should be 1. Changed the acceptor default to 1, with -1 calculating a value based on the number of cores. Updated documentation. Fixed a glitch in ManagedSelector.getMaxSelectedKeys() to return long, not double. Signed-off-by: Simone Bordet <[email protected]>
1 parent 1d5d807 commit 67e2b4a

File tree

9 files changed

+169
-16
lines changed

9 files changed

+169
-16
lines changed

documentation/jetty-documentation/src/main/asciidoc/operations-guide/modules/module-http.adoc

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -29,22 +29,26 @@ The network port that Jetty listens to for clear-text HTTP/1.1 connections -- de
2929
`jetty.http.idleTimeout`::
3030
The amount of time a connection can be idle (i.e. no bytes received and no bytes sent) until the server decides to close it to save resources -- default `30` seconds.
3131
`jetty.http.acceptors`::
32-
The number of threads that compete to accept connections -- default -1 (i.e. an accept heuristic decides the value based on the number of cores).
32+
The number of threads that compete to accept connections -- default 1. Use -1 to let the accept heuristic decides the value; the current heuristic calculates a value based on the number of cores).
33+
Refer to xref:og-module-http-acceptors[this section] for more information about acceptor threads.
3334
`jetty.http.selectors`::
34-
The number of NIO selectors (with an associated thread) that manage connections -- default -1 (i.e. a select heuristic decides the value based on the number of cores).
35+
The number of NIO selectors (with an associated thread) that manage connections -- default -1 (i.e. a select heuristic decides the value; the current heuristic calculates a value based on the number of cores).
3536

3637
[[og-module-http-acceptors]]
3738
====== Configuration of Acceptors
3839

39-
Accepting connections is a blocking operation, so a thread is blocked in the `accept()` call until a connection is accepted, and other threads are blocked on the lock acquired just before the `accept()` call.
40+
Accepting connections from remote clients may be configured as a blocking operation, or a non-blocking operation.
41+
42+
When accepting connections is configured as a blocking operation (the number of acceptors is greater than zero), a thread is blocked in the `accept()` call until a connection is accepted, and other acceptor threads (if any) are blocked on the lock acquired by the accepting thread just before the `accept()` call.
4043

4144
When the accepting thread accepts a connection, it performs a little processing of the just accepted connection, before forwarding it to other components.
4245

4346
During this little processing other connections may be established; if there is only one accepting thread, the newly established connections are waiting for the accepting thread to finish the processing of the previously accepted connection and call again `accept()`.
4447

45-
Servers that manage a very high number of connections that may (naturally) come and go, or that handle inefficient protocols that open and close connections very frequently (such as HTTP/1.0) may benefit of an increased number of acceptor threads.
48+
Servers that manage a very high number of connections that may (naturally) come and go, or that handle inefficient protocols that open and close connections very frequently (such as HTTP/1.0) may benefit of an increased number of acceptor threads, so that when one acceptor thread processes a just accepted connection, another acceptor thread can immediately take over accepting connections.
4649

47-
// TODO: expand on acceptors=0 and non-blocking accepts
50+
When accepting connections is configured as a non-blocking operation (the number of acceptors is zero), then the server socket is set in non-blocking mode and added to a NIO selector.
51+
In this way, no dedicated acceptor threads exist: the work of accepting connections is performed by the selector thread.
4852

4953
[[og-module-http-selectors]]
5054
====== Configuration of Selectors

documentation/jetty-documentation/src/main/asciidoc/operations-guide/modules/module-ssl.adoc

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -29,13 +29,13 @@ Among the configurable properties, the most relevant are:
2929

3030
`jetty.ssl.port`::
3131
The network port that Jetty listens to for secure connections -- default `8443`.
32-
`jetty.http.idleTimeout`::
32+
`jetty.ssl.idleTimeout`::
3333
The amount of time a connection can be idle (i.e. no bytes received and no bytes sent) until the server decides to close it to save resources -- default `30000` milliseconds.
34-
`jetty.http.acceptors`::
35-
The number of threads that compete to accept connections -- default -1 (i.e. an accept heuristic decides the value based on the number of cores).
34+
`jetty.ssl.acceptors`::
35+
The number of threads that compete to accept connections -- default 1. Use -1 to let the accept heuristic decides the value; the current heuristic calculates a value based on the number of cores).
3636
Refer to xref:og-module-http-acceptors[this section] for more information about acceptor threads.
37-
`jetty.http.selectors`::
38-
The number of NIO selectors (with an associated thread) that manage connections -- default -1 (i.e. a select heuristic decides the value based on the number of cores).
37+
`jetty.ssl.selectors`::
38+
The number of NIO selectors (with an associated thread) that manage connections -- default -1 (i.e. a select heuristic decides the value; the current heuristic calculates a value based on the number of cores).
3939
Refer to xref:og-module-http-selectors[this section] for more information about selector threads.
4040

4141
The module properties to configure the KeyStore and TLS parameters are:

jetty-io/src/main/java/org/eclipse/jetty/io/ManagedSelector.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -158,7 +158,7 @@ public double getAverageSelectedKeys()
158158
}
159159

160160
@ManagedAttribute(value = "Maximum number of selected keys", readonly = true)
161-
public double getMaxSelectedKeys()
161+
public long getMaxSelectedKeys()
162162
{
163163
return _keyStats.getMax();
164164
}

jetty-server/src/main/config/etc/jetty-http.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
<Arg>
2323
<New id="httpConnector" class="org.eclipse.jetty.server.ServerConnector">
2424
<Arg name="server"><Ref refid="Server" /></Arg>
25-
<Arg name="acceptors" type="int"><Property name="jetty.http.acceptors" default="-1"/></Arg>
25+
<Arg name="acceptors" type="int"><Property name="jetty.http.acceptors" default="1"/></Arg>
2626
<Arg name="selectors" type="int"><Property name="jetty.http.selectors" default="-1"/></Arg>
2727
<Arg name="factories">
2828
<Array type="org.eclipse.jetty.server.ConnectionFactory">

jetty-server/src/main/config/etc/jetty-ssl.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
<Arg>
1616
<New id="sslConnector" class="org.eclipse.jetty.server.ServerConnector">
1717
<Arg name="server"><Ref refid="Server" /></Arg>
18-
<Arg name="acceptors" type="int"><Property name="jetty.ssl.acceptors" default="-1"/></Arg>
18+
<Arg name="acceptors" type="int"><Property name="jetty.ssl.acceptors" default="1"/></Arg>
1919
<Arg name="selectors" type="int"><Property name="jetty.ssl.selectors" default="-1"/></Arg>
2020
<Arg name="factories">
2121
<Array type="org.eclipse.jetty.server.ConnectionFactory">

jetty-server/src/main/config/modules/http.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ etc/jetty-http.xml
2626
# jetty.http.idleTimeout=30000
2727

2828
## The number of acceptors (-1 picks a default value based on number of cores).
29-
# jetty.http.acceptors=-1
29+
# jetty.http.acceptors=1
3030

3131
## The number of selectors (-1 picks a default value based on number of cores).
3232
# jetty.http.selectors=-1

jetty-server/src/main/config/modules/ssl.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ etc/jetty-ssl-context.xml
2828
# jetty.ssl.idleTimeout=30000
2929

3030
## The number of acceptors (-1 picks a default value based on number of cores).
31-
# jetty.ssl.acceptors=-1
31+
# jetty.ssl.acceptors=1
3232

3333
## The number of selectors (-1 picks a default value based on number of cores).
3434
# jetty.ssl.selectors=-1

jetty-server/src/main/java/org/eclipse/jetty/server/AbstractNetworkConnector.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,6 @@
3030
@ManagedObject("AbstractNetworkConnector")
3131
public abstract class AbstractNetworkConnector extends AbstractConnector implements NetworkConnector
3232
{
33-
3433
private volatile String _host;
3534
private volatile int _port = 0;
3635

Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
//
2+
// ========================================================================
3+
// Copyright (c) 1995-2021 Mort Bay Consulting Pty Ltd and others.
4+
//
5+
// This program and the accompanying materials are made available under the
6+
// terms of the Eclipse Public License v. 2.0 which is available at
7+
// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
8+
// which is available at https://www.apache.org/licenses/LICENSE-2.0.
9+
//
10+
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
11+
// ========================================================================
12+
//
13+
14+
package org.eclipse.jetty.server;
15+
16+
import java.io.IOException;
17+
import java.net.Socket;
18+
import java.nio.charset.StandardCharsets;
19+
import java.util.Collection;
20+
import java.util.concurrent.CountDownLatch;
21+
import java.util.concurrent.CyclicBarrier;
22+
import java.util.concurrent.TimeUnit;
23+
import java.util.stream.IntStream;
24+
import javax.servlet.http.HttpServletRequest;
25+
import javax.servlet.http.HttpServletResponse;
26+
27+
import org.eclipse.jetty.http.HttpStatus;
28+
import org.eclipse.jetty.http.HttpTester;
29+
import org.eclipse.jetty.io.ManagedSelector;
30+
import org.eclipse.jetty.server.handler.AbstractHandler;
31+
import org.junit.jupiter.params.ParameterizedTest;
32+
import org.junit.jupiter.params.provider.ValueSource;
33+
34+
import static org.junit.jupiter.api.Assertions.assertEquals;
35+
import static org.junit.jupiter.api.Assertions.assertNotNull;
36+
import static org.junit.jupiter.api.Assertions.assertTrue;
37+
38+
public class ServerConnectorAcceptTest
39+
{
40+
@ParameterizedTest
41+
@ValueSource(ints = {0, 1, 2})
42+
public void testAccept(int acceptors) throws Exception
43+
{
44+
Server server = new Server();
45+
ServerConnector connector = new ServerConnector(server, acceptors, 1);
46+
server.addConnector(connector);
47+
server.setHandler(new AbstractHandler()
48+
{
49+
@Override
50+
public void handle(String target, Request jettyRequest, HttpServletRequest request, HttpServletResponse response)
51+
{
52+
jettyRequest.setHandled(true);
53+
}
54+
});
55+
server.start();
56+
57+
int runs = 4;
58+
try
59+
{
60+
for (int r = 0; r < runs; ++r)
61+
{
62+
test(acceptors, connector);
63+
}
64+
}
65+
finally
66+
{
67+
server.stop();
68+
}
69+
}
70+
71+
private void test(int acceptors, ServerConnector connector) throws InterruptedException
72+
{
73+
int threads = 8;
74+
int iterations = 4096;
75+
76+
CyclicBarrier barrier = new CyclicBarrier(threads + 1);
77+
CountDownLatch latch = new CountDownLatch(threads * iterations);
78+
IntStream.range(0, threads)
79+
.mapToObj(t -> new Thread(() ->
80+
{
81+
try
82+
{
83+
assertTrue(awaitBarrier(barrier));
84+
85+
long start = System.nanoTime();
86+
for (int i = 0; i < iterations; ++i)
87+
{
88+
try (Socket socket = new Socket("localhost", connector.getLocalPort()))
89+
{
90+
String request = "GET / HTTP/1.1\r\n" +
91+
"Host: localhost\r\n" +
92+
"Connection: close\r\n" +
93+
"\r\n";
94+
socket.getOutputStream().write(request.getBytes(StandardCharsets.UTF_8));
95+
HttpTester.Response response = HttpTester.parseResponse(socket.getInputStream());
96+
assertNotNull(response);
97+
assertEquals(HttpStatus.OK_200, response.getStatus());
98+
}
99+
catch (IOException x)
100+
{
101+
x.printStackTrace();
102+
}
103+
finally
104+
{
105+
latch.countDown();
106+
}
107+
}
108+
long elapsed = System.nanoTime() - start;
109+
System.err.printf("%d acceptors, %d threads, %d requests each, time = %d ms%n",
110+
acceptors,
111+
threads,
112+
iterations,
113+
TimeUnit.NANOSECONDS.toMillis(elapsed));
114+
}
115+
finally
116+
{
117+
assertTrue(awaitBarrier(barrier));
118+
}
119+
}))
120+
.forEach(Thread::start);
121+
122+
// Wait for all the threads to be ready.
123+
assertTrue(awaitBarrier(barrier));
124+
125+
// Wait for all the threads to be finished.
126+
assertTrue(awaitBarrier(barrier));
127+
128+
// Verify that all requests succeeded.
129+
assertTrue(latch.await(15, TimeUnit.SECONDS));
130+
131+
Collection<ManagedSelector> selectors = connector.getSelectorManager().getBeans(ManagedSelector.class);
132+
selectors.stream()
133+
.map(s -> String.format("avg selected keys = %.3f, selects = %d", s.getAverageSelectedKeys(), s.getSelectCount()))
134+
.forEach(System.err::println);
135+
selectors.forEach(ManagedSelector::resetStats);
136+
}
137+
138+
private boolean awaitBarrier(CyclicBarrier barrier)
139+
{
140+
try
141+
{
142+
barrier.await();
143+
return true;
144+
}
145+
catch (Throwable x)
146+
{
147+
return false;
148+
}
149+
}
150+
}

0 commit comments

Comments
 (0)