Netty is a very popular NIO server framework in Java world. It provides excelent performance, but async programming is annoying. Especially after I tried async-await in JavaScript, Goroutine in Go or Coroutine in Kotlin.
As JDK21 became GA on 19th/Sep 2023, Virtual Threads is production ready. I can't wait to try out this new feature in Jetty, which usually not an option for high throughput HTTP application.
Below is a small hello-world application using embeded Jetty 12 and Virtual Threads in JDK 21.
OpenJDK 21 is available in:
https://jdk.java.net/21/
I also recommand you to use sdkman:
sdk install java 21-open
Add Jetty dependnecies into the project.
(I use Maven here, I believe you would know what to do if you use Gradle or other build system)
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>me.imlc</groupId>
<artifactId>virtual-threads-in-jetty</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<maven.compiler.source>21</maven.compiler.source>
<maven.compiler.target>21</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-server</artifactId>
<version>12.0.1</version>
</dependency>
</dependencies>
</project>
jetty-server is the only mandatory dependency for a smallest Jetty application.
To handle a HTTP request, we need a handler.
In a real application, we wouldn't do routing by yourself. We propably use higher level HTTP framework such as SpringBoot in Jetty, Spark Java or Jersey.
To keep it simple, I do it by a couple of simple switch-statement.
Line 14, Thread.sleep(10000);
, is the key here. It simulate a synchronize operation like DB query, which it's totally unacceptable in the old world.
public class HelloHandler extends Handler.Abstract {
@Override
public boolean handle(Request request, Response response, Callback callback) throws Exception {
String path = request.getHttpURI().getPath();
switch (path) {
case "/api/v1/users": {
switch (request.getMethod()) {
case "GET": {
// simulate sync IO operation like writing to database
Thread.sleep(10000);
response.getHeaders().add("Content-Type", "application/json");
// language=JSON
response.write(true, ByteBuffer.wrap("""
{
"data": [
{
"id": 1,
"name": "Alice"
},
{
"id": 2,
"name": "Bob"
}
]
}
""".getBytes(StandardCharsets.UTF_8)), callback);
break;
}
}
break;
}
}
return true;
}
}
Below method starts the Jetty server.
Key point is line 8. It tells Jetty to run in Virtual Threads instead of Platform Threads.
public class Main {
private static final Logger logger = Logger.getLogger("main");
public static void main(String[] args) throws Exception {
QueuedThreadPool threadPool = new QueuedThreadPool();
final int port = 8080;
threadPool.setVirtualThreadsExecutor(VirtualThreads.getDefaultVirtualThreadsExecutor());
Server server = new Server(threadPool);
ServerConnector connector = new ServerConnector(server);
connector.setPort(port);
server.addConnector(connector);
server.setHandler(new HelloHandler());
server.start();
logger.info("Server started at http://localhost:%s/".formatted(port));
}
}
You can also see my GitHub project for the whold Maven project.
https://github.com/lawrenceching/virtual-threads-in-jetty-12
That's all, enjoy.