Java 26 终于原生支持 HTTP/3 了:告别 Netty,直接用 QUIC

欢迎关注微信公众号:FSA全栈行动 👋

一、痛点

以前在 Java 环境里想要搞定 HTTP/3 或者 QUIC 协议,简直是件挺折腾的事。

如果你想在 Java 应用中使用这些现代协议,你基本上没法直接用标准库。为了能处理好 UDP 套接字,你不得不引入像 Netty 或 Vert.x 这样非常"重"的第三方库。这对于只想做一个简单的 HTTP 请求,却不想引入一堆复杂依赖的开发者来说,确实是个负担。

而且,除了依赖问题,还有一个技术硬伤:TCP 的队头阻塞(Head-of-Line Blocking)。

HTTP/2 虽然比 HTTP/1.1 强很多,支持了多路复用,但因为它底层跑的还是 TCP。TCP 要求包必须按顺序到达,这就导致一旦在传输过程中丢了一个包,整个 TCP 连接都会卡住,必须等那个丢掉的包重传成功后,后面的所有请求才能继续。在移动网络或者高延迟场景下,这种表现简直是灾梦。

为了对比,我整理了一个简单的逻辑对比:

特性 HTTP/2 HTTP/3
底层传输 TCP QUIC (UDP)
丢包表现 整个连接卡死(队头阻塞) 仅丢包的 stream 受影响,其他不受干扰
适用场景 稳定、低延迟网络 移动网络、高丢包环境

二、实现:原生支持 HTTP/3

随着 Java 26 的正式发布,JEP 517 终于落地了。这意味着 Java 的标准 HttpClient 现在可以直接支持 HTTP/3 了,再也不用为了 QUIC 去硬啃 Netty 了。

1、如何开启 HTTP/3

因为 HTTP/3 基于 UDP 运行,目前还没法像 TCP 那样在全世界所有防火墙和代理上都跑得通,所以 Java 26 并没有默认开启它,默认还是 HTTP/2。

如果你想用 QUIC,需要显式地在 HttpClient 或者请求构建器里指定版本:

Java 复制代码
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;

public class NativeQuicClient {
    public static void main(String[] args) throws Exception {
        // 在客户端级别显式设置 HTTP/3
        HttpClient client = HttpClient.newBuilder()
                .version(HttpClient.Version.HTTP_3)
                .build();

        HttpRequest request = HttpRequest.newBuilder()
                .uri(URI.create("https://openjdk.org/"))
                .GET()
                .build();

        HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());

        // 输出实际使用的协议版本
        System.out.println("Protocol used: " + response.version());
    }
}

2、Fallback 机制与 Alt-Svc

这里有个细节处理得挺聪明。

如果你配置了 HTTP/3,JVM 会尝试建立 UDP 连接。但如果服务器不支持,或者中间的防火墙把 UDP 给拦了,HttpClient 会自动"降级"回 HTTP/2 或 HTTP/1.1。

而且,Java 26 能够原生解析 Alt-Svc(Alternative Service)响应头。简单来说,当服务器通过 HTTP/2 响应并告知"我其实支持 HTTP/3"时,JVM 会自动在后续请求中切换到 QUIC 协议。这种平滑切换的体验非常好。

三、进阶:强制开启模式

在做微服务架构时,如果你的服务是在可控的内网环境下运行,你可能并不想要那种"先尝试 UDP 失败再降级到 TCP"的检测开销。

这时候你可以通过 Http3DiscoveryMode 来强制要求只使用 HTTP/3。如果 QUIC 连接失败,直接报错,不再尝试 fallback。

Java 复制代码
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpOption;
import java.net.http.Http3DiscoveryMode;

public class StrictQuicClient {
    public static void main(String[] args) throws Exception {
        HttpClient client = HttpClient.newBuilder()
                .version(HttpClient.Version.HTTP_3)
                .build();

        HttpRequest strictRequest = HttpRequest.newBuilder()
                .uri(URI.create("https://internal-api.enterprise.local/"))
                // 开启严格模式:如果 UDP/QUIC 不可用,请求直接失败,不进行 fallback
                .setOption(HttpOption.H3_DISCOVERY, Http3DiscoveryMode.HTTP_3_URI_ONLY)
                .GET()
                .build();

        client.send(strictRequest, HttpResponse.BodyHandlers.ofString());
    }
}

四、最后

JEP 517 的引入,确实让 Java 的网络能力迈进了一大步。

总结一下这次更新带来的收益:

  • 不再需要重型依赖 :不用为了用个 QUIC 就背着一个 Netty 跑。

  • 配置简单 :通过 HttpClient.Version.HTTP_3 就能搞定。

  • 智能化 :自动处理 Alt-Svc 和 TCP 回退。

配合上 Virtual Threads 的高并发能力,现在的 java.net.http 模块在处理高性能网络通信时,表现真的很硬核。

如果文章对您有所帮助, 请不吝点击关注一下我的微信公众号:FSA全栈行动, 这将是对我最大的激励. 公众号不仅有Android技术, 还有iOS, Python等文章, 可能有你想要了解的技能知识点哦~

相关推荐
captain37614 小时前
MyBatis(入门5)其他查询操作
java·数据库·spring boot·mybatis
Novproxy_Z14 小时前
商用代理避坑:如何识别商家拿机房 IP 冒充住宅 IP
java·网络·tcp/ip
文丑颜不良啊15 小时前
LeetCode1481~1500题目解析
java·leetcode
2601_9628857215 小时前
quotes 字段全解:涨跌幅、振幅、换手率的口径与单位
java·前端·spring
前端精髓15 小时前
调用openrouter的免费API模型
java·前端·javascript
步行cgn16 小时前
Spring MVC 拦截器 Interceptor 详解:从执行流程到实战应用
java·spring·mvc
文丑颜不良啊16 小时前
LeetCode1381~1400题目解析
java·leetcode
开开心心_Every16 小时前
快速隐藏程序工具自定义快捷键一键切换
java·开发语言·前端·spring·智能手机·word·intellij-idea
萧瑟余晖17 小时前
Spring Batch核心模型与快速上手详解
java·spring·batch
缓慢更新17 小时前
AI 辅助生成单测:从接口定义到用例的流水线
java·服务器·前端·网络·数据库·人工智能