Skip to content
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
3 changes: 3 additions & 0 deletions frameworks/Java/netty/.sdkmanrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Enable auto-env through the sdkman_auto_env config
# Add key=value pairs of SDKs to use below
java=24-oracle
19 changes: 19 additions & 0 deletions frameworks/Java/netty/benchmark_config.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,25 @@
"display_name": "netty",
"notes": "",
"versus": "netty"
},
"loom": {
"json_url": "/json",
"plaintext_url": "/plaintext",
"port": 8080,
"approach": "Realistic",
"classification": "Platform",
"database": "None",
"framework": "netty",
"language": "Java",
"flavor": "None",
"orm": "Raw",
"platform": "Netty",
"webserver": "None",
"os": "Linux",
"database_os": "Linux",
"display_name": "netty",
"notes": "",
"versus": "netty"
}
}]
}
13 changes: 13 additions & 0 deletions frameworks/Java/netty/config.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,16 @@ orm = "Raw"
platform = "Netty"
webserver = "None"
versus = "netty"

[loom]
urls.plaintext = "/plaintext"
urls.json = "/json"
approach = "Realistic"
classification = "Platform"
database = "None"
database_os = "Linux"
os = "Linux"
orm = "Raw"
platform = "Netty"
webserver = "None"
versus = "netty"
15 changes: 15 additions & 0 deletions frameworks/Java/netty/netty-loom.dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
FROM maven:3.9.9-eclipse-temurin-24-noble as maven
WORKDIR /netty
COPY pom.xml pom.xml
COPY src src
RUN mvn compile assembly:single -q

FROM maven:3.9.9-eclipse-temurin-24-noble
WORKDIR /netty
COPY --from=maven /netty/target/app.jar app.jar
COPY run_netty_loom.sh run_netty_loom.sh

EXPOSE 8080
# see https://github.com/netty/netty/issues/14942
# remember to run this with --privileged since https://github.com/TechEmpower/FrameworkBenchmarks/blob/c94f7f95bd751f86a57dea8b63fb8f336bdbbde3/toolset/utils/docker_helper.py#L239 does it
ENTRYPOINT "./run_netty_loom.sh"
12 changes: 7 additions & 5 deletions frameworks/Java/netty/netty.dockerfile
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
FROM maven:3.6.1-jdk-11-slim as maven
FROM maven:3.9.9-eclipse-temurin-24-noble as maven
WORKDIR /netty
COPY pom.xml pom.xml
COPY src src
RUN mvn compile assembly:single -q

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

EXPOSE 8080

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"]
# see https://github.com/netty/netty/issues/14942
# remember to run this with --privileged since https://github.com/TechEmpower/FrameworkBenchmarks/blob/c94f7f95bd751f86a57dea8b63fb8f336bdbbde3/toolset/utils/docker_helper.py#L239 does it
ENTRYPOINT "./run_netty.sh"
17 changes: 9 additions & 8 deletions frameworks/Java/netty/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,9 @@
<version>0.1</version>

<properties>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
<netty.version>4.1.108.Final</netty.version>
<io_uring.version>0.0.21.Final</io_uring.version>
<maven.compiler.source>24</maven.compiler.source>
<maven.compiler.target>24</maven.compiler.target>
<netty.version>4.2.0.Final</netty.version>
</properties>

<packaging>jar</packaging>
Expand All @@ -21,7 +20,7 @@

<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-codec-http</artifactId>
<artifactId>netty-all</artifactId>
<version>${netty.version}</version>
</dependency>

Expand All @@ -40,9 +39,9 @@
</dependency>

<dependency>
<groupId>io.netty.incubator</groupId>
<artifactId>netty-incubator-transport-native-io_uring</artifactId>
<version>${io_uring.version}</version>
<groupId>io.netty</groupId>
<artifactId>netty-transport-native-io_uring</artifactId>
<version>${netty.version}</version>
<classifier>linux-x86_64</classifier>
</dependency>

Expand Down Expand Up @@ -74,6 +73,7 @@
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<configuration>
<finalName>app</finalName>
<archive>
<manifest>
<mainClass>hello.HelloWebServer</mainClass>
Expand All @@ -82,6 +82,7 @@
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
<appendAssemblyId>false</appendAssemblyId>
</configuration>
<executions>
<execution>
Expand Down
14 changes: 14 additions & 0 deletions frameworks/Java/netty/run_netty.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
#!/bin/bash

# PROFILING: -XX:+UnlockDiagnosticVMOptions -XX:+DebugNonSafepoints
JAVA_OPTIONS="--enable-native-access=ALL-UNNAMED \
-Dio.netty.noUnsafe=false \
--sun-misc-unsafe-memory-access=allow \
--add-opens=java.base/java.lang=ALL-UNNAMED \
-XX:+UseNUMA \
-XX:+UseParallelGC \
-Dio.netty.buffer.checkBounds=false \
-Dio.netty.buffer.checkAccessible=false \
$@"

java $JAVA_OPTIONS -jar app.jar
18 changes: 18 additions & 0 deletions frameworks/Java/netty/run_netty_loom.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
#!/bin/bash

# PROFILING: -XX:+UnlockDiagnosticVMOptions -XX:+DebugNonSafepoints
JAVA_OPTIONS="--enable-native-access=ALL-UNNAMED \
-Dio.netty.noUnsafe=false \
--sun-misc-unsafe-memory-access=allow \
--add-opens=java.base/java.lang=ALL-UNNAMED \
-XX:+UseNUMA \
-XX:+UseParallelGC \
-Dio.netty.buffer.checkBounds=false \
-Dio.netty.buffer.checkAccessible=false \
-Dhello.eventloop.carrier=true \
-XX:+UnlockExperimentalVMOptions \
-XX:-DoJVMTIVirtualThreadTransitions \
-Djdk.trackAllThreads=false \
$@"

java $JAVA_OPTIONS -jar app.jar
24 changes: 24 additions & 0 deletions frameworks/Java/netty/src/main/java/hello/Constants.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package hello;

import io.netty.util.AsciiString;
import io.netty.util.CharsetUtil;

public class Constants {

public static final byte[] STATIC_PLAINTEXT = "Hello, World!".getBytes(CharsetUtil.UTF_8);
public static final int STATIC_PLAINTEXT_LEN = STATIC_PLAINTEXT.length;

public static final CharSequence PLAINTEXT_CLHEADER_VALUE = AsciiString.cached(String.valueOf(STATIC_PLAINTEXT_LEN));
public static final int JSON_LEN = jsonLen();
public static final CharSequence JSON_CLHEADER_VALUE = AsciiString.cached(String.valueOf(JSON_LEN));
public static final CharSequence SERVER_NAME = AsciiString.cached("Netty");

private static int jsonLen() {
return JsonUtils.serializeMsg(newMsg()).length;
}

public static Message newMsg() {
return new Message("Hello, World!");
}

}
99 changes: 29 additions & 70 deletions frameworks/Java/netty/src/main/java/hello/HelloServerHandler.java
Original file line number Diff line number Diff line change
@@ -1,28 +1,20 @@
package hello;

import static io.netty.handler.codec.http.HttpHeaderNames.CONTENT_LENGTH;
import static io.netty.handler.codec.http.HttpHeaderNames.CONTENT_TYPE;
import static io.netty.handler.codec.http.HttpHeaderNames.DATE;
import static io.netty.handler.codec.http.HttpHeaderNames.SERVER;
import static io.netty.handler.codec.http.HttpHeaderValues.APPLICATION_JSON;
import static io.netty.handler.codec.http.HttpHeaderValues.TEXT_PLAIN;
import static hello.HttpResponses.makeJsonResponse;
import static hello.HttpResponses.makePlaintextResponse;
import static hello.JsonUtils.acquireJsonStreamFromEventLoop;
import static hello.JsonUtils.releaseJsonStreamFromEventLoop;
import static io.netty.handler.codec.http.HttpResponseStatus.NOT_FOUND;
import static io.netty.handler.codec.http.HttpResponseStatus.OK;
import static io.netty.handler.codec.http.HttpVersion.HTTP_1_1;

import java.io.IOException;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Date;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

import com.jsoniter.output.JsonStream;
import com.jsoniter.output.JsonStreamPool;
import com.jsoniter.spi.JsonException;

import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext;
Expand All @@ -33,51 +25,21 @@
import io.netty.handler.codec.http.HttpRequest;
import io.netty.handler.codec.http.LastHttpContent;
import io.netty.util.AsciiString;
import io.netty.util.CharsetUtil;
import io.netty.util.ReferenceCountUtil;
import io.netty.util.concurrent.FastThreadLocal;

public class HelloServerHandler extends ChannelInboundHandlerAdapter {

private static final FastThreadLocal<DateFormat> FORMAT = new FastThreadLocal<DateFormat>() {
@Override
protected DateFormat initialValue() {
return new SimpleDateFormat("E, dd MMM yyyy HH:mm:ss z");
}
};

private static Message newMsg() {
return new Message("Hello, World!");
}

private static byte[] serializeMsg(Message obj) {
JsonStream stream = JsonStreamPool.borrowJsonStream();
try {
stream.reset(null);
stream.writeVal(Message.class, obj);
return Arrays.copyOfRange(stream.buffer().data(), 0, stream.buffer().tail());
} catch (IOException e) {
throw new JsonException(e);
} finally {
JsonStreamPool.returnJsonStream(stream);
}
}

private static int jsonLen() {
return serializeMsg(newMsg()).length;
}
private static final FastThreadLocal<DateFormat> FORMAT = new FastThreadLocal<>() {
@Override
protected DateFormat initialValue() {
return new SimpleDateFormat("E, dd MMM yyyy HH:mm:ss z");
}
};

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

private static final CharSequence PLAINTEXT_CLHEADER_VALUE = AsciiString.cached(String.valueOf(STATIC_PLAINTEXT_LEN));
private static final int JSON_LEN = jsonLen();
private static final CharSequence JSON_CLHEADER_VALUE = AsciiString.cached(String.valueOf(JSON_LEN));
private static final CharSequence SERVER_NAME = AsciiString.cached("Netty");

private volatile CharSequence date = new AsciiString(FORMAT.get().format(new Date()));

HelloServerHandler(ScheduledExecutorService service) {
public HelloServerHandler(ScheduledExecutorService service) {
service.scheduleWithFixedDelay(new Runnable() {
private final DateFormat format = FORMAT.get();

Expand Down Expand Up @@ -118,42 +80,39 @@ private void process(ChannelHandlerContext ctx, HttpRequest request) throws Exce
String uri = request.uri();
switch (uri) {
case "/plaintext":
writePlainResponse(ctx, Unpooled.wrappedBuffer(STATIC_PLAINTEXT));
writePlainResponse(ctx, date);
return;
case "/json":
byte[] json = serializeMsg(newMsg());
writeJsonResponse(ctx, Unpooled.wrappedBuffer(json));
// even for the virtual thread case we expect virtual threads to be executed inlined!
var stream = acquireJsonStreamFromEventLoop();
try {
writeJsonResponse(ctx, stream, date);
} finally {
releaseJsonStreamFromEventLoop(stream);
}
return;
}
// we drain in-flight responses before closing the connection
channelReadComplete(ctx);
FullHttpResponse response = new DefaultFullHttpResponse(HTTP_1_1, NOT_FOUND, Unpooled.EMPTY_BUFFER, false);
ctx.write(response).addListener(ChannelFutureListener.CLOSE);
}

private void writePlainResponse(ChannelHandlerContext ctx, ByteBuf buf) {
ctx.write(makeResponse(buf, TEXT_PLAIN, PLAINTEXT_CLHEADER_VALUE), ctx.voidPromise());
ctx.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE);
}

private void writeJsonResponse(ChannelHandlerContext ctx, ByteBuf buf) {
ctx.write(makeResponse(buf, APPLICATION_JSON, JSON_CLHEADER_VALUE), ctx.voidPromise());
protected void writePlainResponse(ChannelHandlerContext ctx, AsciiString date) {
ctx.write(makePlaintextResponse(date), ctx.voidPromise());
}

private FullHttpResponse makeResponse(ByteBuf buf, CharSequence contentType, CharSequence contentLength) {
final FullHttpResponse response = new DefaultFullHttpResponse(HTTP_1_1, OK, buf, false);
response.headers()
.set(CONTENT_TYPE, contentType)
.set(SERVER, SERVER_NAME)
.set(DATE, date)
.set(CONTENT_LENGTH, contentLength);
return response;
protected void writeJsonResponse(ChannelHandlerContext ctx, JsonStream stream, AsciiString date) {
ctx.write(makeJsonResponse(stream, date), ctx.voidPromise());
}

@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
ctx.close();
}

@Override
public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
public void channelReadComplete(ChannelHandlerContext ctx) {
ctx.flush();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@

public class HelloServerInitializer extends ChannelInitializer<SocketChannel> {

private final ScheduledExecutorService service;
protected final ScheduledExecutorService service;

public HelloServerInitializer(ScheduledExecutorService service) {
this.service = service;
Expand Down Expand Up @@ -46,6 +46,10 @@ protected boolean isContentAlwaysEmpty(final HttpMessage msg) {
return false;
}
})
.addLast("handler", new HelloServerHandler(service));
.addLast("handler", newHelloServerHandler(service));
}

protected HelloServerHandler newHelloServerHandler(ScheduledExecutorService service) {
return new HelloServerHandler(service);
}
}
Loading
Loading