You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

寻求基于Jetty10/11嵌入式的全功能HTTP/2客户端-服务端可运行实现参考

嵌入式Jetty 11 全量HTTP/2特性端到端实现

前置准备

  • 采用Jetty 11.0.24长期支持稳定版,无外部容器依赖,所有逻辑通过main方法在localhost本地直接运行
  • 覆盖核心能力:STREAMS多路复用、低层帧交互、PUSH_PROMISE服务端推送、HPACK头部压缩、ALPN协议协商

本地HTTPS/2 SSL证书生成步骤

HTTP/2强制要求基于TLS+ALPN实现,无需第三方CA,使用JDK自带keytool即可生成满足本地运行要求的自签名证书:

  1. 生成服务端密钥库
    keytool -genkeypair -alias localhost -keyalg RSA -keysize 2048 -validity 3650 -keystore server-keystore.p12 -storetype PKCS12 -storepass changeit -keypass changeit -dname "CN=localhost,OU=Dev,O=Local,L=Beijing,ST=Beijing,C=CN" -ext "SAN=dns:localhost,ip:127.0.0.1"
    
  2. 导出证书并生成客户端信任库
    # 导出服务端证书
    keytool -exportcert -alias localhost -keystore server-keystore.p12 -storetype PKCS12 -storepass changeit -file localhost.cer
    # 导入证书到客户端信任库
    keytool -importcert -alias localhost -file localhost.cer -keystore client-truststore.p12 -storetype PKCS12 -storepass changeit -noprompt
    

注意:证书必须配置SAN扩展包含localhost和127.0.0.1,否则SSL握手阶段会因域名不匹配直接失败

项目依赖配置

Maven项目直接引入以下稳定版依赖,无版本兼容问题:

<dependencies>
    <!-- Jetty 11 核心服务端组件 -->
    <dependency>
        <groupId>org.eclipse.jetty</groupId>
        <artifactId>jetty-server</artifactId>
        <version>11.0.24</version>
    </dependency>
    <!-- Jetty 11 HTTP/2 服务端实现 -->
    <dependency>
        <groupId>org.eclipse.jetty.http2</groupId>
        <artifactId>jetty-http2-server</artifactId>
        <version>11.0.24</version>
    </dependency>
    <!-- JVM 内置ALPN实现,替代旧版外部boot jar依赖 -->
    <dependency>
        <groupId>org.eclipse.jetty</groupId>
        <artifactId>jetty-alpn-java-server</artifactId>
        <version>11.0.24</version>
    </dependency>
    <!-- Jetty 11 HTTP/2 客户端实现 -->
    <dependency>
        <groupId>org.eclipse.jetty.http2</groupId>
        <artifactId>jetty-http2-client</artifactId>
        <version>11.0.24</version>
    </dependency>
    <!-- 客户端ALPN支持 -->
    <dependency>
        <groupId>org.eclipse.jetty</groupId>
        <artifactId>jetty-alpn-java-client</artifactId>
        <version>11.0.24</version>
    </dependency>
    <!-- Servlet 上下文支持 -->
    <dependency>
        <groupId>org.eclipse.jetty</groupId>
        <artifactId>jetty-servlet</artifactId>
        <version>11.0.24</version>
    </dependency>
</dependencies>

服务端实现(全特性配置)

核心能力配置

  • 流管理:设置单连接最大并发流数100,初始流接收窗口65535字节,连接级接收窗口1048576字节
  • HPACK配置:设置HPACK动态表最大容量4096字节,与客户端参数自动协商
  • PUSH_PROMISE:显式开启服务端推送能力,允许单请求最多推送10个关联资源
  • 低层帧监听:挂载全局帧监听器,捕获所有入站/出站的SETTINGS、HEADERS、DATA、PUSH_PROMISE、WINDOW_UPDATE、RST_STREAM帧,无黑盒抽象

关键代码片段

// 初始化HTTP/2核心配置
HTTP2ServerConnectionFactory http2 = new HTTP2ServerConnectionFactory(new HttpConfiguration());
http2.setMaxConcurrentStreams(100);
http2.setInitialStreamRecvWindow(65535);
http2.setInitialSessionRecvWindow(1048576);
http2.setPushEnabled(true);
http2.setMaxPushHeaders(10);
http2.setHpackContextFactory(context -> context.setMaxTableCapacity(4096));

// 挂载低层帧监听器
http2.addBean(new FrameListener() {
    @Override
    public void onOpen(Session session) {
        System.out.println("HTTP/2 会话建立,远程端点: " + session.getRemoteSocketAddress());
    }
    @Override
    public void onFrame(Session session, Stream stream, Frame frame) {
        System.out.printf("捕获低层帧: 类型=%s, 流ID=%d, 负载长度=%d%n",
            frame.getType().name(),
            stream != null ? stream.getId() : 0,
            frame.getLength());
    }
});

// 配置ALPN协商顺序
ALPNServerConnectionFactory alpn = new ALPNServerConnectionFactory();
alpn.setDefaultProtocol("h2");

// 加载SSL证书
SslContextFactory sslContextFactory = new SslContextFactory.Server();
sslContextFactory.setKeyStorePath("server-keystore.p12");
sslContextFactory.setKeyStorePassword("changeit");
SslConnectionFactory ssl = new SslConnectionFactory(sslContextFactory, alpn.getProtocol());

// 组装连接器,监听8443端口
ServerConnector connector = new ServerConnector(server, ssl, alpn, http2);
connector.setPort(8443);

// 注册根路径Servlet,触发服务端推送
ServletContextHandler context = new ServletContextHandler();
context.addServlet(HomeServlet.class, "/");
// 在HomeServlet中实现推送逻辑:收到根路径请求后,向客户端推送/static/style.css、/static/app.js,主动生成PUSH_PROMISE帧
context.addServlet(StaticResourceServlet.class, "/static/*");
server.addConnector(connector);
server.setHandler(context);
server.start();

客户端实现(特性验证)

核心验证逻辑

  • 加载之前生成的client-truststore.p12,完成SSL证书信任校验
  • 挂载帧监听器,捕获所有交互帧,重点监听PUSH_PROMISE帧、HPACK头部长度变化
  • 单条SSL连接上并发发起10个不同路径的请求,验证STREAMS多路复用能力
  • 重复发起相同路径请求,对比两次HEADERS帧长度,验证HPACK压缩生效
  • 接收服务端推送的静态资源,验证PUSH_PROMISE帧的完整交互

关键代码片段

// 初始化HTTP/2客户端
HTTP2Client http2Client = new HTTP2Client();
http2Client.setConnectTimeout(5000);

// 配置客户端SSL信任
SslContextFactory.Client sslContextFactory = new SslContextFactory.Client();
sslContextFactory.setTrustStorePath("client-truststore.p12");
sslContextFactory.setTrustStorePassword("changeit");

// 挂载客户端帧监听器
http2Client.addBean(new FrameListener() {
    @Override
    public void onPush(Session session, Stream stream, PushPromiseFrame frame) {
        String pushPath = HttpURI.from(frame.getMetaData().getPath());
        System.out.printf("接收到PUSH_PROMISE帧: 关联流ID=%d, 推送资源=%s%n",
            stream.getId(), pushPath);
    }
});

// 初始化HTTP客户端
HttpClientTransport transport = new HttpClientTransportOverHTTP2(http2Client);
HttpClient client = new HttpClient(transport, sslContextFactory);
client.start();

// 在单连接上并发发起10个请求
CountDownLatch latch = new CountDownLatch(10);
for (int i = 0; i < 10; i++) {
    client.newRequest("https://localhost:8443/test/" + i)
        .send(result -> {
            System.out.printf("请求完成: 路径=%s, 状态码=%d, 复用连接: %s%n",
                result.getRequest().getPath(),
                result.getResponse().getStatus(),
                result.getConnection().getLocalAddress());
            latch.countDown();
        });
}
latch.await();

运行验证流程

  1. 启动服务端main方法,控制台会输出ALPN协商成功、SETTINGS帧交互日志,确认HPACK参数、最大流数协商完成
  2. 启动客户端main方法,依次确认:
    • 10个并发请求全部复用同一条SSL连接,对应流ID从1开始递增,无新建TCP连接,证明STREAMS多路复用生效
    • 第二次发起相同路径请求时,HEADERS帧长度比首次请求减少30%以上,证明HPACK动态表压缩生效
    • 控制台输出接收到两个静态资源的PUSH_PROMISE帧,且对应推送资源的响应自动完成传输
    • 帧监听器打印全量HTTP/2帧类型的交互记录,覆盖协议定义的所有核心帧

常见踩坑规避

  • 不使用Jetty 9.x版本:9.x版本ALPN依赖外部boot jar包,极易出现JDK版本不兼容问题,Jetty 11内置JVM ALPN实现无需额外配置
  • 连接窗口参数设置不低于64KB,否则会出现大响应流阻塞、传输超时问题
  • 客户端与服务端的HPACK动态表大小保持兼容,否则会出现头部解码错误

内容的提问来源于stack exchange,提问作者Zipperz

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.25 14:06:04