Boost Web Performance: Java HTTP/3 Benchmark vs HTTP/2
This article explains how Java 24 and OpenJDK add HTTP/3 support, compares its speed to HTTP/2 using benchmark code, and shows why the QUIC‑based protocol delivers lower latency and fewer TCP connections for modern web applications.
Let's discuss Java's improvements for modern web communication: HTTP/3 (or HTTP/3) aims to achieve faster web communication, improving on HTTP/2 and older HTTP/1.
Java 24 and OpenJDK introduce HTTP/3 to the HTTP client API via JEP 517.
Getting Started with HTTP/3
HTTP/3 is the latest evolution of the Hypertext Transfer Protocol for exchanging information on the Web. It retains the same semantics—request/response/session/status codes—but encodes them differently and maintains session state in a different way.
Earlier HTTP/2 and HTTP/1 assumed a stateful network connection (TCP/IP) beneath the client and server, guaranteeing reliable, ordered packet delivery. This caused delays when a packet was lost, requiring retransmission and queueing.
Why is HTTP/3 Faster than HTTP/2?
HTTP/3 replaces the slower TCP protocol in the TLS layer with the faster QUIC protocol.
QUIC (pronounced “KWIK”) was originally designed at Google; today most browsers—Chrome, Edge, Firefox, Safari—support it.
What Is the Underlying QUIC Protocol?
QUIC improves performance of connection‑oriented network applications by establishing multiplexed connections between endpoints. It uses UDP, aiming to replace TCP in the transport layer.
Although UDP lacks built‑in reliability, ordering, and flow control, recent enhancements make it suitable for network communication.
How QUIC Improves TLS and Replaces TCP
In HTTP/2, multiple streams share a single TCP connection; if one packet is delayed, all subsequent packets are delayed. In QUIC, packets from different streams arrive independently, so a delay in one stream does not block others.
QUIC efficiently manages multiplexed connections, reduces latency, and performs bandwidth estimation to avoid congestion.
Several Java implementations of QUIC or HTTP/3 exist, such as KWIK, Quiche4j, Flupke, and Jetty.
Why Use Jetty‑Based Client/Server Classes?
Although most browsers support HTTP/3, some web servers still do not. Jetty is one server that implements HTTP/3 for both client and server sides, though its implementation can be verbose.
We therefore use Kwik, a Java wrapper around QUIC, and optionally Jetty (v12+).
What Are Kwik and Flupke?
They provide pure‑Java wrappers for the QUIC protocol, avoiding the need to handle low‑level sockets.
KWIK implements the QUIC protocol (RFC 9000) in pure Java for both client and server.
Flupke builds on Kwik to provide a Java HTTP/3 implementation, simplifying usage.
Below is a simple HTTP/2 benchmark program.
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.time.Duration;
import java.util.List;
public class AnachronisticHttpBenchmark {
public static void main(String[] args) throws Exception {
List<String> urls = List.of(
"https://reutersinstitute.politics.ox.ac.uk/digital-news-report/2024/dnr-executive-summary",
"https://www.nytimes.com/2024/12/26/briefing/the-year-in-news.html",
"https://www.heraldnet.com/news/the-top-10-most-read-herald-stories-of-2024",
"https://www.cbsnews.com/news/top-news-headlines-of-2024-month-by-month",
"https://www.cfr.org/article/ten-most-significant-world-events-2024"
);
HttpClient client = HttpClient.newBuilder()
.followRedirects(HttpClient.Redirect.NORMAL)
.connectTimeout(Duration.ofSeconds(10))
.build();
for (String url : urls) {
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(url))
.GET()
.build();
long startTime = System.nanoTime();
HttpResponse<byte[]> response = client.send(request, HttpResponse.BodyHandlers.ofByteArray());
long endTime = System.nanoTime();
long durationMillis = (endTime - startTime) / 1_000_000;
int responseSizeBytes = response.body().length;
System.out.printf("URL: %s%n", url);
System.out.printf("Status: %d%n", response.statusCode());
System.out.printf("Response Time: %d ms%n", durationMillis);
System.out.printf("Response Size: %d bytes%n", responseSizeBytes);
System.out.println("=".repeat(60));
}
}
}Now we repeat the same operation with an HTTP/3 client to compare performance.
import tech.kwik.flupke.Http3Client;
import tech.kwik.flupke.Http3ClientBuilder;
import java.io.IOException;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.time.Duration;
import java.util.List;
public class Sample {
public static void main(String[] args) throws IOException, InterruptedException {
List<String> urls = List.of(
"https://reutersinstitute.politics.ox.ac.uk/digital-news-report/2024/dnr-executive-summary",
"https://www.nytimes.com/2024/12/26/briefing/the-year-in-news.html",
"https://www.heraldnet.com/news/the-top-10-most-read-herald-stories-of-2024/",
"https://www.cbsnews.com/news/top-news-headlines-of-2024-month-by-month",
"https://www.cfr.org/article/ten-most-significant-world-events-2024"
);
try {
for (String url : urls) {
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(url))
.header("User-Agent", "Flupke http3 library")
.timeout(Duration.ofSeconds(1000))
.build();
HttpClient client = Http3Client.newHttpClient();
long start = System.currentTimeMillis();
HttpResponse<String> httpResponse = client.send(request, HttpResponse.BodyHandlers.ofString());
long end = System.currentTimeMillis();
reportResult(httpResponse, end - start);
}
} catch (IOException e) {
System.err.println("Request failed: " + e.getMessage());
} catch (InterruptedException e) {
System.err.println("Request interrupted: " + e.getMessage());
}
}
private static void reportResult(HttpResponse<String> httpResponse, long duration) throws IOException {
System.out.printf("Status: %d%n", httpResponse.statusCode());
System.out.printf("Response Size: %d bytes%n", httpResponse.body().length());
System.out.println("Request completed in " + duration + " ms");
System.out.println("=".repeat(60));
}
}Even with only a few URLs, the HTTP/3 client consistently shows faster response times.
Figure: Response Time Comparison
The benchmark class almost always retrieves responses in fewer milliseconds.
When an entire web application uses HTTP/3 sockets, performance gains can be substantial.
If your server also supports HTTP/3, the improvement and reduced network latency will be even greater.
Results may vary based on network speed and hardware, but the overall trend matches our observations.
Our yc‑script 360° snapshot and yCrash tools also analyzed network performance of the two classes.
Simple HTTP connections used more TCP/IP connections, many of which remained in a WAIT state due to lost packets.
Figure: Network Report – Simple HTTP Connections
In contrast, HTTP/3 classes created fewer TCP/IP connections, relying mainly on faster UDP connections, with only one thread in WAIT state.
Figure: Network Report – HTTP/3 Connections
Conclusion: HTTP/3 vs HTTP/2 and HTTP/1
HTTP/3 uses fewer network resources and works with faster UDP connections, resulting in fewer connections in the WAIT state.
This indicates that network work completes more quickly, reducing latency.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Cognitive Technology Team
Cognitive Technology Team regularly delivers the latest IT news, original content, programming tutorials and experience sharing, with daily perks awaiting you.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
