Skip to content

Commit 6443b71

Browse files
authored
Upgrade to JDK 24, Netty 4.2 and Virtual Thread Experimental support (#9800)
* Upgrade to JDK 24, Netty 4.2 and Virtual Thread Experimental support * Improving Virtual Thread performance
1 parent 520d2bb commit 6443b71

20 files changed

+672
-128
lines changed

frameworks/Java/netty/.sdkmanrc

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# Enable auto-env through the sdkman_auto_env config
2+
# Add key=value pairs of SDKs to use below
3+
java=24-oracle

frameworks/Java/netty/benchmark_config.json

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,25 @@
1919
"display_name": "netty",
2020
"notes": "",
2121
"versus": "netty"
22+
},
23+
"loom": {
24+
"json_url": "/json",
25+
"plaintext_url": "/plaintext",
26+
"port": 8080,
27+
"approach": "Realistic",
28+
"classification": "Platform",
29+
"database": "None",
30+
"framework": "netty",
31+
"language": "Java",
32+
"flavor": "None",
33+
"orm": "Raw",
34+
"platform": "Netty",
35+
"webserver": "None",
36+
"os": "Linux",
37+
"database_os": "Linux",
38+
"display_name": "netty",
39+
"notes": "",
40+
"versus": "netty"
2241
}
2342
}]
2443
}

frameworks/Java/netty/config.toml

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,3 +13,16 @@ orm = "Raw"
1313
platform = "Netty"
1414
webserver = "None"
1515
versus = "netty"
16+
17+
[loom]
18+
urls.plaintext = "/plaintext"
19+
urls.json = "/json"
20+
approach = "Realistic"
21+
classification = "Platform"
22+
database = "None"
23+
database_os = "Linux"
24+
os = "Linux"
25+
orm = "Raw"
26+
platform = "Netty"
27+
webserver = "None"
28+
versus = "netty"
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
FROM maven:3.9.9-eclipse-temurin-24-noble as maven
2+
WORKDIR /netty
3+
COPY pom.xml pom.xml
4+
COPY src src
5+
RUN mvn compile assembly:single -q
6+
7+
FROM maven:3.9.9-eclipse-temurin-24-noble
8+
WORKDIR /netty
9+
COPY --from=maven /netty/target/app.jar app.jar
10+
COPY run_netty_loom.sh run_netty_loom.sh
11+
12+
EXPOSE 8080
13+
# see https://github.com/netty/netty/issues/14942
14+
# remember to run this with --privileged since https://github.com/TechEmpower/FrameworkBenchmarks/blob/c94f7f95bd751f86a57dea8b63fb8f336bdbbde3/toolset/utils/docker_helper.py#L239 does it
15+
ENTRYPOINT "./run_netty_loom.sh"
Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,15 @@
1-
FROM maven:3.6.1-jdk-11-slim as maven
1+
FROM maven:3.9.9-eclipse-temurin-24-noble as maven
22
WORKDIR /netty
33
COPY pom.xml pom.xml
44
COPY src src
55
RUN mvn compile assembly:single -q
66

7-
FROM openjdk:11.0.3-jdk-slim
7+
FROM maven:3.9.9-eclipse-temurin-24-noble
88
WORKDIR /netty
9-
COPY --from=maven /netty/target/netty-example-0.1-jar-with-dependencies.jar app.jar
9+
COPY --from=maven /netty/target/app.jar app.jar
10+
COPY run_netty.sh run_netty.sh
1011

1112
EXPOSE 8080
12-
13-
CMD ["java", "-server", "-XX:+UseNUMA", "-XX:+UseParallelGC", "-XX:+AggressiveOpts", "-Dio.netty.buffer.checkBounds=false", "-Dio.netty.buffer.checkAccessible=false", "-Dio.netty.iouring.iosqeAsyncThreshold=32000", "-jar", "app.jar"]
13+
# see https://github.com/netty/netty/issues/14942
14+
# remember to run this with --privileged since https://github.com/TechEmpower/FrameworkBenchmarks/blob/c94f7f95bd751f86a57dea8b63fb8f336bdbbde3/toolset/utils/docker_helper.py#L239 does it
15+
ENTRYPOINT "./run_netty.sh"

frameworks/Java/netty/pom.xml

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,9 @@
99
<version>0.1</version>
1010

1111
<properties>
12-
<maven.compiler.source>11</maven.compiler.source>
13-
<maven.compiler.target>11</maven.compiler.target>
14-
<netty.version>4.1.108.Final</netty.version>
15-
<io_uring.version>0.0.21.Final</io_uring.version>
12+
<maven.compiler.source>24</maven.compiler.source>
13+
<maven.compiler.target>24</maven.compiler.target>
14+
<netty.version>4.2.0.Final</netty.version>
1615
</properties>
1716

1817
<packaging>jar</packaging>
@@ -21,7 +20,7 @@
2120

2221
<dependency>
2322
<groupId>io.netty</groupId>
24-
<artifactId>netty-codec-http</artifactId>
23+
<artifactId>netty-all</artifactId>
2524
<version>${netty.version}</version>
2625
</dependency>
2726

@@ -40,9 +39,9 @@
4039
</dependency>
4140

4241
<dependency>
43-
<groupId>io.netty.incubator</groupId>
44-
<artifactId>netty-incubator-transport-native-io_uring</artifactId>
45-
<version>${io_uring.version}</version>
42+
<groupId>io.netty</groupId>
43+
<artifactId>netty-transport-native-io_uring</artifactId>
44+
<version>${netty.version}</version>
4645
<classifier>linux-x86_64</classifier>
4746
</dependency>
4847

@@ -74,6 +73,7 @@
7473
<plugin>
7574
<artifactId>maven-assembly-plugin</artifactId>
7675
<configuration>
76+
<finalName>app</finalName>
7777
<archive>
7878
<manifest>
7979
<mainClass>hello.HelloWebServer</mainClass>
@@ -82,6 +82,7 @@
8282
<descriptorRefs>
8383
<descriptorRef>jar-with-dependencies</descriptorRef>
8484
</descriptorRefs>
85+
<appendAssemblyId>false</appendAssemblyId>
8586
</configuration>
8687
<executions>
8788
<execution>

frameworks/Java/netty/run_netty.sh

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
#!/bin/bash
2+
3+
# PROFILING: -XX:+UnlockDiagnosticVMOptions -XX:+DebugNonSafepoints
4+
JAVA_OPTIONS="--enable-native-access=ALL-UNNAMED \
5+
-Dio.netty.noUnsafe=false \
6+
--sun-misc-unsafe-memory-access=allow \
7+
--add-opens=java.base/java.lang=ALL-UNNAMED \
8+
-XX:+UseNUMA \
9+
-XX:+UseParallelGC \
10+
-Dio.netty.buffer.checkBounds=false \
11+
-Dio.netty.buffer.checkAccessible=false \
12+
$@"
13+
14+
java $JAVA_OPTIONS -jar app.jar
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
#!/bin/bash
2+
3+
# PROFILING: -XX:+UnlockDiagnosticVMOptions -XX:+DebugNonSafepoints
4+
JAVA_OPTIONS="--enable-native-access=ALL-UNNAMED \
5+
-Dio.netty.noUnsafe=false \
6+
--sun-misc-unsafe-memory-access=allow \
7+
--add-opens=java.base/java.lang=ALL-UNNAMED \
8+
-XX:+UseNUMA \
9+
-XX:+UseParallelGC \
10+
-Dio.netty.buffer.checkBounds=false \
11+
-Dio.netty.buffer.checkAccessible=false \
12+
-Dhello.eventloop.carrier=true \
13+
-XX:+UnlockExperimentalVMOptions \
14+
-XX:-DoJVMTIVirtualThreadTransitions \
15+
-Djdk.trackAllThreads=false \
16+
$@"
17+
18+
java $JAVA_OPTIONS -jar app.jar
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
package hello;
2+
3+
import io.netty.util.AsciiString;
4+
import io.netty.util.CharsetUtil;
5+
6+
public class Constants {
7+
8+
public static final byte[] STATIC_PLAINTEXT = "Hello, World!".getBytes(CharsetUtil.UTF_8);
9+
public static final int STATIC_PLAINTEXT_LEN = STATIC_PLAINTEXT.length;
10+
11+
public static final CharSequence PLAINTEXT_CLHEADER_VALUE = AsciiString.cached(String.valueOf(STATIC_PLAINTEXT_LEN));
12+
public static final int JSON_LEN = jsonLen();
13+
public static final CharSequence JSON_CLHEADER_VALUE = AsciiString.cached(String.valueOf(JSON_LEN));
14+
public static final CharSequence SERVER_NAME = AsciiString.cached("Netty");
15+
16+
private static int jsonLen() {
17+
return JsonUtils.serializeMsg(newMsg()).length;
18+
}
19+
20+
public static Message newMsg() {
21+
return new Message("Hello, World!");
22+
}
23+
24+
}
Lines changed: 29 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,20 @@
11
package hello;
22

3-
import static io.netty.handler.codec.http.HttpHeaderNames.CONTENT_LENGTH;
4-
import static io.netty.handler.codec.http.HttpHeaderNames.CONTENT_TYPE;
5-
import static io.netty.handler.codec.http.HttpHeaderNames.DATE;
6-
import static io.netty.handler.codec.http.HttpHeaderNames.SERVER;
7-
import static io.netty.handler.codec.http.HttpHeaderValues.APPLICATION_JSON;
8-
import static io.netty.handler.codec.http.HttpHeaderValues.TEXT_PLAIN;
3+
import static hello.HttpResponses.makeJsonResponse;
4+
import static hello.HttpResponses.makePlaintextResponse;
5+
import static hello.JsonUtils.acquireJsonStreamFromEventLoop;
6+
import static hello.JsonUtils.releaseJsonStreamFromEventLoop;
97
import static io.netty.handler.codec.http.HttpResponseStatus.NOT_FOUND;
10-
import static io.netty.handler.codec.http.HttpResponseStatus.OK;
118
import static io.netty.handler.codec.http.HttpVersion.HTTP_1_1;
129

13-
import java.io.IOException;
1410
import java.text.DateFormat;
1511
import java.text.SimpleDateFormat;
16-
import java.util.Arrays;
1712
import java.util.Date;
1813
import java.util.concurrent.ScheduledExecutorService;
1914
import java.util.concurrent.TimeUnit;
2015

2116
import com.jsoniter.output.JsonStream;
22-
import com.jsoniter.output.JsonStreamPool;
23-
import com.jsoniter.spi.JsonException;
2417

25-
import io.netty.buffer.ByteBuf;
2618
import io.netty.buffer.Unpooled;
2719
import io.netty.channel.ChannelFutureListener;
2820
import io.netty.channel.ChannelHandlerContext;
@@ -33,51 +25,21 @@
3325
import io.netty.handler.codec.http.HttpRequest;
3426
import io.netty.handler.codec.http.LastHttpContent;
3527
import io.netty.util.AsciiString;
36-
import io.netty.util.CharsetUtil;
3728
import io.netty.util.ReferenceCountUtil;
3829
import io.netty.util.concurrent.FastThreadLocal;
3930

4031
public class HelloServerHandler extends ChannelInboundHandlerAdapter {
4132

42-
private static final FastThreadLocal<DateFormat> FORMAT = new FastThreadLocal<DateFormat>() {
43-
@Override
44-
protected DateFormat initialValue() {
45-
return new SimpleDateFormat("E, dd MMM yyyy HH:mm:ss z");
46-
}
47-
};
48-
49-
private static Message newMsg() {
50-
return new Message("Hello, World!");
51-
}
52-
53-
private static byte[] serializeMsg(Message obj) {
54-
JsonStream stream = JsonStreamPool.borrowJsonStream();
55-
try {
56-
stream.reset(null);
57-
stream.writeVal(Message.class, obj);
58-
return Arrays.copyOfRange(stream.buffer().data(), 0, stream.buffer().tail());
59-
} catch (IOException e) {
60-
throw new JsonException(e);
61-
} finally {
62-
JsonStreamPool.returnJsonStream(stream);
63-
}
64-
}
65-
66-
private static int jsonLen() {
67-
return serializeMsg(newMsg()).length;
68-
}
33+
private static final FastThreadLocal<DateFormat> FORMAT = new FastThreadLocal<>() {
34+
@Override
35+
protected DateFormat initialValue() {
36+
return new SimpleDateFormat("E, dd MMM yyyy HH:mm:ss z");
37+
}
38+
};
6939

70-
private static final byte[] STATIC_PLAINTEXT = "Hello, World!".getBytes(CharsetUtil.UTF_8);
71-
private static final int STATIC_PLAINTEXT_LEN = STATIC_PLAINTEXT.length;
40+
protected volatile AsciiString date = new AsciiString(FORMAT.get().format(new Date()));
7241

73-
private static final CharSequence PLAINTEXT_CLHEADER_VALUE = AsciiString.cached(String.valueOf(STATIC_PLAINTEXT_LEN));
74-
private static final int JSON_LEN = jsonLen();
75-
private static final CharSequence JSON_CLHEADER_VALUE = AsciiString.cached(String.valueOf(JSON_LEN));
76-
private static final CharSequence SERVER_NAME = AsciiString.cached("Netty");
77-
78-
private volatile CharSequence date = new AsciiString(FORMAT.get().format(new Date()));
79-
80-
HelloServerHandler(ScheduledExecutorService service) {
42+
public HelloServerHandler(ScheduledExecutorService service) {
8143
service.scheduleWithFixedDelay(new Runnable() {
8244
private final DateFormat format = FORMAT.get();
8345

@@ -118,42 +80,39 @@ private void process(ChannelHandlerContext ctx, HttpRequest request) throws Exce
11880
String uri = request.uri();
11981
switch (uri) {
12082
case "/plaintext":
121-
writePlainResponse(ctx, Unpooled.wrappedBuffer(STATIC_PLAINTEXT));
83+
writePlainResponse(ctx, date);
12284
return;
12385
case "/json":
124-
byte[] json = serializeMsg(newMsg());
125-
writeJsonResponse(ctx, Unpooled.wrappedBuffer(json));
86+
// even for the virtual thread case we expect virtual threads to be executed inlined!
87+
var stream = acquireJsonStreamFromEventLoop();
88+
try {
89+
writeJsonResponse(ctx, stream, date);
90+
} finally {
91+
releaseJsonStreamFromEventLoop(stream);
92+
}
12693
return;
12794
}
95+
// we drain in-flight responses before closing the connection
96+
channelReadComplete(ctx);
12897
FullHttpResponse response = new DefaultFullHttpResponse(HTTP_1_1, NOT_FOUND, Unpooled.EMPTY_BUFFER, false);
129-
ctx.write(response).addListener(ChannelFutureListener.CLOSE);
130-
}
131-
132-
private void writePlainResponse(ChannelHandlerContext ctx, ByteBuf buf) {
133-
ctx.write(makeResponse(buf, TEXT_PLAIN, PLAINTEXT_CLHEADER_VALUE), ctx.voidPromise());
98+
ctx.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE);
13499
}
135100

136-
private void writeJsonResponse(ChannelHandlerContext ctx, ByteBuf buf) {
137-
ctx.write(makeResponse(buf, APPLICATION_JSON, JSON_CLHEADER_VALUE), ctx.voidPromise());
101+
protected void writePlainResponse(ChannelHandlerContext ctx, AsciiString date) {
102+
ctx.write(makePlaintextResponse(date), ctx.voidPromise());
138103
}
139104

140-
private FullHttpResponse makeResponse(ByteBuf buf, CharSequence contentType, CharSequence contentLength) {
141-
final FullHttpResponse response = new DefaultFullHttpResponse(HTTP_1_1, OK, buf, false);
142-
response.headers()
143-
.set(CONTENT_TYPE, contentType)
144-
.set(SERVER, SERVER_NAME)
145-
.set(DATE, date)
146-
.set(CONTENT_LENGTH, contentLength);
147-
return response;
105+
protected void writeJsonResponse(ChannelHandlerContext ctx, JsonStream stream, AsciiString date) {
106+
ctx.write(makeJsonResponse(stream, date), ctx.voidPromise());
148107
}
149108

150109
@Override
151-
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
110+
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
152111
ctx.close();
153112
}
154113

155114
@Override
156-
public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
115+
public void channelReadComplete(ChannelHandlerContext ctx) {
157116
ctx.flush();
158117
}
159118
}

0 commit comments

Comments
 (0)