Skip to content

Commit f2a8bde

Browse files
committed
netty/jetty: review performance degradation on 4.x
- fix #3771 - ref #3770
1 parent e25382c commit f2a8bde

File tree

6 files changed

+110
-37
lines changed

6 files changed

+110
-37
lines changed
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
/*
2+
* Jooby https://jooby.io
3+
* Apache License Version 2.0 https://jooby.io/LICENSE.txt
4+
* Copyright 2014 Edgar Espina
5+
*/
6+
package io.jooby.internal.netty;
7+
8+
import java.util.concurrent.TimeUnit;
9+
10+
import io.netty.channel.EventLoopGroup;
11+
12+
public class NettyEventLoopGroup {
13+
private final EventLoopGroup parent;
14+
private final EventLoopGroup child;
15+
private boolean closed;
16+
17+
public NettyEventLoopGroup(NettyTransport transport, boolean single, int ioThreads) {
18+
child = transport.createEventLoop(ioThreads, "eventloop", 100);
19+
if (single) {
20+
parent = child;
21+
} else {
22+
parent = transport.createEventLoop(1, "acceptor", 50);
23+
}
24+
}
25+
26+
public EventLoopGroup getParent() {
27+
return parent;
28+
}
29+
30+
public EventLoopGroup getChild() {
31+
return child;
32+
}
33+
34+
public void shutdown() {
35+
if (!closed) {
36+
closed = true;
37+
if (parent == child) {
38+
shutdown(child, 0);
39+
} else {
40+
try {
41+
shutdown(parent, 0);
42+
} finally {
43+
shutdown(child, 2);
44+
}
45+
}
46+
}
47+
}
48+
49+
private void shutdown(EventLoopGroup eventLoopGroup, int quietPeriod) {
50+
eventLoopGroup.shutdownGracefully(quietPeriod, 15, TimeUnit.SECONDS);
51+
}
52+
}

modules/jooby-netty/src/main/java/io/jooby/internal/netty/NettyHandler.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,13 +39,13 @@ public class NettyHandler extends ChannelInboundHandlerAdapter {
3939
private NettyContext context;
4040

4141
public NettyHandler(
42-
NettyDateService dateService,
42+
NettyDateService serverDate,
4343
List<Jooby> applications,
4444
long maxRequestSize,
4545
int bufferSize,
4646
boolean defaultHeaders,
4747
boolean http2) {
48-
this.serverDate = dateService;
48+
this.serverDate = serverDate;
4949
this.applications = applications;
5050
this.ctxSelector = Context.Selector.create(applications);
5151
this.maxRequestSize = maxRequestSize;

modules/jooby-netty/src/main/java/io/jooby/internal/netty/NettyPipeline.java

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
package io.jooby.internal.netty;
77

88
import java.util.List;
9+
import java.util.concurrent.ScheduledExecutorService;
910
import java.util.function.Supplier;
1011

1112
import io.jooby.Jooby;
@@ -20,7 +21,6 @@
2021
public class NettyPipeline extends ChannelInitializer<SocketChannel> {
2122
private static final String H2_HANDSHAKE = "h2-handshake";
2223
private final SslContext sslContext;
23-
private final NettyDateService serverDate;
2424
private final HttpDecoderConfig decoderConfig;
2525
private final List<Jooby> applications;
2626
private final long maxRequestSize;
@@ -32,7 +32,6 @@ public class NettyPipeline extends ChannelInitializer<SocketChannel> {
3232

3333
public NettyPipeline(
3434
SslContext sslContext,
35-
NettyDateService dateService,
3635
HttpDecoderConfig decoderConfig,
3736
List<Jooby> applications,
3837
long maxRequestSize,
@@ -42,7 +41,6 @@ public NettyPipeline(
4241
boolean expectContinue,
4342
Integer compressionLevel) {
4443
this.sslContext = sslContext;
45-
this.serverDate = dateService;
4644
this.decoderConfig = decoderConfig;
4745
this.applications = applications;
4846
this.maxRequestSize = maxRequestSize;
@@ -53,9 +51,14 @@ public NettyPipeline(
5351
this.compressionLevel = compressionLevel;
5452
}
5553

56-
private NettyHandler createHandler() {
54+
private NettyHandler createHandler(ScheduledExecutorService executor) {
5755
return new NettyHandler(
58-
serverDate, applications, maxRequestSize, bufferSize, defaultHeaders, http2);
56+
new NettyDateService(executor),
57+
applications,
58+
maxRequestSize,
59+
bufferSize,
60+
defaultHeaders,
61+
http2);
5962
}
6063

6164
@Override
@@ -79,7 +82,7 @@ public void initChannel(SocketChannel ch) {
7982

8083
p.addLast(H2_HANDSHAKE, handshake);
8184
additionalHandlers(p);
82-
p.addLast("handler", createHandler());
85+
p.addLast("handler", createHandler(ch.eventLoop()));
8386
} else {
8487
http11(p);
8588
}
@@ -121,6 +124,6 @@ private void http11(ChannelPipeline p) {
121124
p.addLast("decoder", new NettyRequestDecoder(decoderConfig));
122125
p.addLast("encoder", new NettyResponseEncoder());
123126
additionalHandlers(p);
124-
p.addLast("handler", createHandler());
127+
p.addLast("handler", createHandler(p.channel().eventLoop()));
125128
}
126129
}

modules/jooby-netty/src/main/java/io/jooby/netty/NettyServer.java

Lines changed: 25 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -52,16 +52,13 @@ public class NettyServer extends Server.Base {
5252
System.setProperty("__server_.name", NAME);
5353
}
5454

55-
private static final int _50 = 50;
56-
57-
private static final int _100 = 100;
58-
59-
private EventLoopGroup acceptorloop;
60-
private EventLoopGroup eventloop;
55+
private NettyEventLoopGroup eventLoop;
6156
private ExecutorService worker;
6257

6358
private List<Jooby> applications;
6459
private NettyOutputFactory outputFactory;
60+
private boolean singleEventLoopGroup =
61+
System.getProperty("io.netty.eventLoopGroup", "parent-child").equals("single");
6562

6663
/**
6764
* Creates a server.
@@ -90,6 +87,18 @@ public NettyServer(@NonNull ServerOptions options) {
9087

9188
public NettyServer() {}
9289

90+
/**
91+
* Configure netty to use a single event loop group. When true, the acceptor and eventLoop share
92+
* the same {@link EventLoopGroup}. Default is <code>true</code>.
93+
*
94+
* @param value True for shared {@link EventLoopGroup}.
95+
* @return This server.
96+
*/
97+
public NettyServer setSingleEventLoopGroup(boolean value) {
98+
this.singleEventLoopGroup = value;
99+
return this;
100+
}
101+
93102
@Override
94103
public OutputFactory getOutputFactory() {
95104
if (outputFactory == null) {
@@ -127,22 +136,14 @@ public Server start(@NonNull Jooby... application) {
127136
var classLoader = applications.get(0).getClassLoader();
128137

129138
var transport = NettyTransport.transport(classLoader);
130-
131-
/* Acceptor event-loop */
132-
// this.acceptorloop = transport.createEventLoop(1, "acceptor", _50);
133-
134-
/* Event loop: processing connections, parsing messages and doing engine's internal work */
135-
this.eventloop = transport.createEventLoop(options.getIoThreads(), "eventloop", _100);
136-
this.acceptorloop = eventloop;
137-
var dateService = new NettyDateService(eventloop);
139+
eventLoop = new NettyEventLoopGroup(transport, singleEventLoopGroup, options.getIoThreads());
138140

139141
var outputFactory = (NettyOutputFactory) getOutputFactory();
140142
var allocator = outputFactory.getAllocator();
141143
var http2 = options.isHttp2() == Boolean.TRUE;
142144
/* Bootstrap: */
143145
if (!options.isHttpsOnly()) {
144-
var http =
145-
newBootstrap(allocator, transport, newPipeline(options, null, dateService, http2));
146+
var http = newBootstrap(allocator, transport, newPipeline(options, null, http2), eventLoop);
146147
http.bind(options.getHost(), options.getPort()).get();
147148
}
148149

@@ -155,8 +156,7 @@ public Server start(@NonNull Jooby... application) {
155156
var clientAuth = sslOptions.getClientAuth();
156157
var sslContext = wrap(javaSslContext, toClientAuth(clientAuth), protocol, http2);
157158
var https =
158-
newBootstrap(
159-
allocator, transport, newPipeline(options, sslContext, dateService, http2));
159+
newBootstrap(allocator, transport, newPipeline(options, sslContext, http2), eventLoop);
160160
portInUse = options.getSecurePort();
161161
https.bind(options.getHost(), portInUse).get();
162162
} else if (options.isHttpsOnly()) {
@@ -177,9 +177,12 @@ public Server start(@NonNull Jooby... application) {
177177
}
178178

179179
private ServerBootstrap newBootstrap(
180-
ByteBufAllocator allocator, NettyTransport transport, NettyPipeline factory) {
180+
ByteBufAllocator allocator,
181+
NettyTransport transport,
182+
NettyPipeline factory,
183+
NettyEventLoopGroup group) {
181184
return transport
182-
.configure(acceptorloop, eventloop)
185+
.configure(eventLoop.getParent(), eventLoop.getChild())
183186
.childHandler(factory)
184187
.childOption(ChannelOption.ALLOCATOR, allocator)
185188
.childOption(ChannelOption.SO_REUSEADDR, true)
@@ -194,8 +197,7 @@ private ClientAuth toClientAuth(SslOptions.ClientAuth clientAuth) {
194197
};
195198
}
196199

197-
private NettyPipeline newPipeline(
198-
ServerOptions options, SslContext sslContext, NettyDateService dateService, boolean http2) {
200+
private NettyPipeline newPipeline(ServerOptions options, SslContext sslContext, boolean http2) {
199201
var decoderConfig =
200202
new HttpDecoderConfig()
201203
.setMaxInitialLineLength(_4KB)
@@ -205,7 +207,6 @@ private NettyPipeline newPipeline(
205207
.setTrailersFactory(HEADERS);
206208
return new NettyPipeline(
207209
sslContext,
208-
dateService,
209210
decoderConfig,
210211
applications,
211212
options.getMaxRequestSize(),
@@ -222,9 +223,7 @@ public synchronized Server stop() {
222223
// only for jooby build where close events may take longer.
223224
NettyWebSocket.all.clear();
224225

225-
// required after Netty 4.2
226-
shutdown(acceptorloop, 0);
227-
shutdown(eventloop, 2);
226+
eventLoop.shutdown();
228227
if (worker != null) {
229228
worker.shutdown();
230229
worker = null;

tests/pom.xml

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -383,4 +383,23 @@
383383
</plugin>
384384
</plugins>
385385
</build>
386+
387+
<profiles>
388+
<profile>
389+
<id>netty-mac_aarch_64</id>
390+
<activation>
391+
<os>
392+
<family>mac</family>
393+
</os>
394+
</activation>
395+
<dependencies>
396+
<dependency>
397+
<groupId>io.netty</groupId>
398+
<artifactId>netty-transport-native-kqueue</artifactId>
399+
<version>${netty.version}</version>
400+
<classifier>osx-aarch_64</classifier>
401+
</dependency>
402+
</dependencies>
403+
</profile>
404+
</profiles>
386405
</project>

tests/src/test/java/examples/Performance.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
import io.jooby.Jooby;
1515
import io.jooby.ServerOptions;
1616
import io.jooby.StartupSummary;
17-
import io.jooby.jetty.JettyServer;
17+
import io.jooby.netty.NettyServer;
1818

1919
public class Performance extends Jooby {
2020

@@ -43,6 +43,6 @@ public class Performance extends Jooby {
4343

4444
public static void main(final String[] args) {
4545
System.setProperty("io.netty.disableHttpHeadersValidation", "true");
46-
runApp(args, new JettyServer(new ServerOptions()), EVENT_LOOP, Performance::new);
46+
runApp(args, new NettyServer(new ServerOptions()), EVENT_LOOP, Performance::new);
4747
}
4848
}

0 commit comments

Comments
 (0)