Why HTTP/2 Beats HTTP/1.1 in NDSF: Deep Dive and Performance Benchmarks
This article examines the limitations of HTTP/1.x, explains how HTTP/2 addresses them, details the NDSF framework's implementation choices, presents comprehensive JMeter‑based performance tests, and analyzes the results to show traffic reduction, latency stability, and overall business benefits.
Introduction
HTTP/1.1 has been stable for over a decade but suffers from connection‑reuse limits, head‑of‑line (HOL) blocking, large header overhead, and lack of encryption. HTTP/2, built on SPDY3, introduces binary framing, HPACK header compression, request multiplexing, and mandatory TLS, aiming to use a single connection between client and server.
Problems with HTTP/1.x
Connection reuse : each request may require a new TCP handshake and slow‑start, increasing latency.
Head‑of‑line blocking : responses must be sent in request order, causing bandwidth under‑utilisation.
Protocol overhead : large, often unchanged headers increase transmission cost.
Security : data is sent in clear text without authentication.
HTTP/2 Solutions
Connection multiplexing : a single connection carries multiple streams, eliminating extra handshakes.
Elimination of HOL blocking : frames can be sent out of order and reassembled on the receiver side.
Header compression : HPACK reduces header size.
Improved security : browsers only support HTTP/2 over HTTPS.
NDSF HTTP/2 Implementation Choices
Protocol Variant
h2: HTTP/2 over TLS, requires certificates and adds CPU load. h2c: clear‑text HTTP/2 over TCP, no TLS configuration needed. NDSF adopts h2c to avoid certificate management and reduce CPU usage.
JDK & Embedded Container
Spring Boot 2.0.3+ with Java 8+ can embed the following servers:
Apache Tomcat 8.5 (requires libtcnative)
Eclipse Jetty 9.4.11
Undertow 1.4.25
NDSF selects Jetty for its stability while staying on JDK 8.
Server Implementation (Jetty)
Configure Jetty connectors for HTTP/2 clear‑text:
HTTP2CServerConnectionFactory http2c = new HTTP2CServerConnectionFactory(config);
HttpConnectionFactory http = new HttpConnectionFactory(config);
ServerConnector connector = new ServerConnector(server, http, http2c);
connector.setPort(this.getPort());
server.addConnector(connector);Set up a custom Http2JettyServletWebServerFactory with thread pool, port and context path:
Http2JettyServletWebServerFactory factory = new Http2JettyServletWebServerFactory(maxFormContentSize, enableRequestLog, requestLogPath);
factory.setThreadPool(new QueuedThreadPool(maxThreads, minThreads, threadPoolIdleTimeout,
new LinkedBlockingQueue<Runnable>(queueSize)));
factory.setPort(port);
factory.setContextPath("/*");
return factory;Client Implementation (Jetty HTTP/2 client)
HTTP2Client h2Client = new HTTP2Client();
HttpClientTransportOverHTTP2 transport = new HttpClientTransportOverHTTP2(h2Client);
HttpClient http2cClient = new HttpClient(transport, null);Send a request and obtain the response content:
org.eclipse.jetty.client.api.Request req = getHttpClient(config).newRequest(url.toString());
FutureResponseListener listener = new FutureResponseListener(req);
req.send(listener);
ContentResponse response = listener.get(config.getReadTimeoutMillis(), TimeUnit.MILLISECONDS);
return response.getContent();Testing Schemes
Scheme 1 : JMeter drives a consumer to call the provider via an HTTP interface.
Scheme 2 : Consumer unchanged; JUnit directly invokes provider methods, removing HTTP overhead.
Scheme 3 : Extend JMeter with AbstractJavaSamplerClient for method‑level load testing.
Test Environment
JVM: JDK 1.8
Provider machine: 32 CPU cores, 48 GB RAM
Consumer machines (6 cloud hosts): 4 CPU cores, 8 GB RAM each
Measured Results
QPS
1024 B payload – HTTP/2: up to 45 573.8 req/s; HTTP/1.1: up to 48 656.4 req/s
512 B payload – HTTP/2: up to 49 051.9 req/s; HTTP/1.1: up to 51 242.2 req/s
Low concurrency favors HTTP/2; high concurrency slightly favors HTTP/1.1.
99% Latency
Payload size has little impact.
For the same payload, HTTP/1.1 shows lower 99% latency than HTTP/2.
Maximum Latency
HTTP/2 max latency stays in milliseconds; HTTP/1.1 can reach seconds.
HTTP/2 consistently lower max latency for identical payloads.
Traffic
HTTP/2 reduces traffic dramatically due to HPACK header compression.
Analysis
Traffic reduction stems from HPACK’s static and dynamic tables and optional Huffman coding, which compress request/response headers. QPS similarity is explained by persistent connections and HTTP pipelining in HTTP/1.1, which mitigates handshake overhead but still suffers from ordering constraints. HTTP/2’s true multiplexing eliminates head‑of‑line blocking, providing more stable performance under high concurrency.
Framework Issues
Jetty may send a GOAWAY frame without graceful shutdown, causing AsynchronousCloseException (does not affect normal operation).
Switching Jetty versions after a client‑provider connection is established can break HTTP/2 streams.
Complete HTTP/2 Handshake Flow
Client adds Upgrade: h2c header when server capability is unknown.
Browser (or client) sends an HTTP/2‑Settings frame (base64‑encoded) during TLS handshake for HTTPS.
If the server supports HTTP/2, it replies with 101 Switching Protocols and begins sending HTTP/2 frames.
Server sends the connection preface with a SETTINGS frame.
Server creates a new stream, optionally pushes resources (e.g., a.js), then sends index.html and associated frames.
Browser receives PUSH_PROMISE, finds the resource cached, and sends RST_STREAM to reject the push.
Server stops sending the pushed resource after receiving RST_STREAM.
When finished, either side may send GOAWAY to close the connection.
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.
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.
