1. 概述

HTTP/2 协议最引人注目的特性之一是 服务端推送(Server Push),它允许服务器在客户端发起一次请求后,主动推送多个相关资源。✅
这直接减少了传统 HTTP/1.x 中因多次往返(round-trip)获取资源带来的延迟,显著提升页面加载速度。

Jetty 作为成熟的 Java Web 服务器,对 HTTP/2 提供了完整的客户端和服务端支持。

本文将深入 Jetty 的 HTTP/2 实现机制,并通过一个实战示例,带你直观感受 Server Push 带来的性能飞跃。重点聚焦 如何配置、验证及踩坑提示,避免走弯路。

2. 快速上手

2.1. 下载与环境准备

运行 Jetty 的 HTTP/2 功能,需满足两个硬性条件:

  • ✅ JDK 8 或更高版本
  • ✅ ALPN(Application-Layer Protocol Negotiation)支持

⚠️ 注意:HTTP/2 在非加密场景(HTTP/2 Clear Text, h2c)支持有限,生产环境强烈建议通过 SSL/TLS 启用 ALPN 来协商 h2 协议

操作步骤:

  1. 下载最新版 Jetty
  2. 解压后设置环境变量 JETTY_HOME 指向解压目录

2.2. 启用 HTTP/2 连接器

使用 Jetty 的模块化启动机制,一行命令即可激活 HTTP/2:

java -jar $JETTY_HOME/start.jar --add-to-start=http2

该命令会:

  • 自动在 8443 端口的 SSL 连接器上启用 HTTP/2
  • 递归激活 alpnssl 等依赖模块

执行后输出日志如下,重点关注 http2alpn 模块:

INFO  : server          transitively enabled, ini template available with --add-to-start=server
INFO  : alpn-impl/alpn-1.8.0_131 dynamic dependency of alpn-impl/alpn-8
INFO  : alpn-impl       transitively enabled
INFO  : alpn            transitively enabled, ini template available with --add-to-start=alpn
INFO  : http2           initialized in ${jetty.base}/start.ini
INFO  : ssl             transitively enabled, ini template available with --add-to-start=ssl
INFO  : Base directory was modified

2.3. 启动 Jetty 服务

执行启动命令:

java -jar $JETTY_HOME/start.jar

成功启动后,日志会明确显示连接器信息:

INFO::main: Logging initialized @228ms to org.eclipse.jetty.util.log.StdErrLog
...
INFO:oejs.AbstractConnector:main: Started ServerConnector@42dafa95{SSL, (ssl, alpn, h2)}{0.0.0.0:8443}
INFO:oejs.Server:main: Started @872ms

✅ 关键点:{SSL, (ssl, alpn, h2)} 表示 SSL 连接器已支持 h2 协议。

2.4. 启用其他模块

若需同时支持 HTTP/1.1 和 h2c(明文 HTTP/2),可追加模块:

java -jar $JETTY_HOME/start.jar --add-to-start=http,http2c

验证日志,你会看到两个连接器并存:

INFO:oejs.AbstractConnector:main: Started ServerConnector@6adede5{SSL, (ssl, alpn, h2)}{0.0.0.0:8443}
INFO:oejs.AbstractConnector:main: Started ServerConnector@dc24521{HTTP/1.1, (http/1.1, h2c)}{0.0.0.0:8080}

查看所有可用模块:

java -jar $JETTY_HOME/start.jar --list-modules

输出会列出 http2alpn 等模块的依赖和配置文件,是排查问题的好帮手。

2.5. 高级配置

使用 --list-config 查看模块对应的 XML 配置文件:

java -jar $JETTY_HOME/start.jar --list-config

start.ini 中可自定义关键参数:

# SSL 连接配置
jetty.ssl.host=0.0.0.0
jetty.ssl.port=8443
jetty.ssl.idleTimeout=30000

# HTTP/2 性能调优
jetty.http2.maxConcurrentStreams=128
jetty.http2.initialStreamRecvWindow=524288
jetty.http2.initialSessionRecvWindow=1048576
jetty.http2.maxSettingsKeys=64
jetty.http2.rateControl.maxEventsPerSecond=20

⚠️ 踩坑提示:maxConcurrentStreams 过小可能导致并发推送被限流,生产环境建议根据负载测试调整。

3. 构建 Jetty HTTP/2 应用

3.1. Maven 依赖配置

pom.xml 中引入核心依赖:

<build>
    <plugins>
        <plugin>
            <groupId>org.eclipse.jetty</groupId>
            <artifactId>jetty-maven-plugin</artifactId>
            <version>9.4.27.v20200227</version>
            <dependencies>
                <!-- HTTP/2 服务端实现 -->
                <dependency>
                    <groupId>org.eclipse.jetty.http2</groupId>
                    <artifactId>http2-server</artifactId>
                    <version>9.4.27.v20200227</version>
                </dependency>
                <!-- JDK 8 ALPN 支持 -->
                <dependency>
                    <groupId>org.eclipse.jetty</groupId>
                    <artifactId>jetty-alpn-openjdk8-server</artifactId>
                    <version>9.4.27.v20200227</version>
                </dependency>
                <!-- 包含 PushCacheFilter -->
                <dependency>
                    <groupId>org.eclipse.jetty</groupId>
                    <artifactId>jetty-servlets</artifactId>
                    <version>9.4.27.v20200227</version>
                </dependency>
            </dependencies>
        </plugin>
    </plugins>
</build>

编译并启动:

mvn clean package
mvn jetty:run-forked

此时服务默认以 HTTP/1.1 在 8080 端口运行。

3.2. 通过 jetty.xml 配置 HTTP/2

src/main/config/jetty.xml 中手动定义 HTTP/2 连接器:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure_9_0.dtd">
<Configure id="Server" class="org.eclipse.jetty.server.Server">
    <!-- sslContextFactory 和 httpConfig 定义省略 -->

    <Call name="addConnector">
        <Arg>
            <New class="org.eclipse.jetty.server.ServerConnector">
                <Arg name="server"><Ref id="Server"/></Arg>
                <Arg name="factories">
                    <Array type="org.eclipse.jetty.server.ConnectionFactory">
                        <!-- SSL 工厂,next 指向 alpn -->
                        <Item>
                            <New class="org.eclipse.jetty.server.SslConnectionFactory">
                                <Arg name="sslContextFactory"><Ref id="sslContextFactory"/></Arg>
                                <Arg name="next">alpn</Arg>
                            </New>
                        </Item>
                        <!-- ALPN 工厂,协商协议优先级 -->
                        <Item>
                            <New class="org.eclipse.jetty.alpn.server.ALPNServerConnectionFactory">
                                <Arg>h2</Arg>
                            </New>
                        </Item>
                        <!-- HTTP/2 服务端工厂 -->
                        <Item>
                            <New class="org.eclipse.jetty.http2.server.HTTP2ServerConnectionFactory">
                                <Arg name="config"><Ref id="httpConfig"/></Arg>
                            </New>
                        </Item>
                    </Array>
                </Arg>
                <Set name="port">8444</Set>
            </New>
        </Arg>
    </Call>
</Configure>

pom.xml 插件中指定配置文件路径:

<plugin>
    <groupId>org.eclipse.jetty</groupId>
    <artifactId>jetty-maven-plugin</artifactId>
    <version>9.4.27.v20200227</version>
    <configuration>
        <stopPort>8888</stopPort>
        <stopKey>quit</stopKey>
        <!-- JDK 8 必须添加 alpn-boot jar 到 BootClasspath -->
        <jvmArgs>
            -Xbootclasspath/p:${settings.localRepository}/org/mortbay/jetty/alpn/alpn-boot/8.1.11.v20170118/alpn-boot-8.1.11.v20170118.jar
        </jvmArgs>
        <jettyXml>${basedir}/src/main/config/jetty.xml</jettyXml>
        <webApp>
            <contextPath>/</contextPath>
        </webApp>
    </configuration>
</plugin>

⚠️ 踩坑提示:JDK 8 需手动引入 alpn-boot jar,JDK 9+ 已内置 ALPN,无需此步骤。

重启应用,日志应显示:

oejs.AbstractConnector:main: Started ServerConnector@1810399e{SSL, (ssl, alpn, h2)}{0.0.0.0:8444}

3.3. 配置 PushCacheFilter

使用 Jetty 内置的 PushCacheFilter 实现资源预推送:

<filter>
    <filter-name>push</filter-name>
    <filter-class>org.eclipse.jetty.servlets.PushCacheFilter</filter-class>
    <init-param>
        <param-name>ports</param-name>
        <param-value>8444</param-value> <!-- 仅对 HTTP/2 端口生效 -->
    </init-param>
</filter>
<filter-mapping>
    <filter-name>push</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

该 Filter 会自动缓存主资源(如 index.html)关联的静态资源(CSS, JS, 图片),并在响应主资源时主动推送。

3.4. Servlet 配置

创建处理图片请求的 Servlet 并映射路径:

<servlet>
    <servlet-name>http2Jetty</servlet-name>
    <servlet-class>com.baeldung.jetty.http2.Http2JettyServlet</servlet-class>
</servlet>
<servlet-mapping>
    <servlet-name>http2Jetty</servlet-name>
    <url-pattern>/images/*</url-pattern>
</servlet-mapping>

4. 构建 HTTP/2 客户端测试页

创建 http2.html,引用多个图片资源以测试推送效果:

<!DOCTYPE html>
<html>
<head>
    <title>Jetty HTTP/2 测试</title>
</head>
<body>
    <h2>HTTP/2 Demo</h2>
    <div>
        <img src="images/homepage-latest_articles.jpg" alt="latest articles" />
        <img src="images/homepage-rest_with_spring.jpg" alt="rest with spring" />
        <img src="images/homepage-weekly_reviews.jpg" alt="weekly reviews" />
    </div>
</body>
</html>

5. 测试与结果验证

基线测试(HTTP/1.1)

访问 https://localhost:8443/http2.html,打开浏览器开发者工具(Network 标签页):

http2 screenshot 1

观察到:

  • 协议为 http/1.1
  • 图片资源需单独发起请求
  • 加载耗时约 3-6ms

HTTP/2 + Push 测试

访问 https://localhost:8444/http2.html

http2 screenshot 2

关键变化:

  • ✅ 协议显示为 h2
  • ✅ 图片资源的 Initiator 为 Push,表明由服务端主动推送
  • ✅ 所有图片加载时间降至 1ms

✅ 结论:PushCacheFilter 成功将关联的静态资源预先推送到客户端,页面加载性能得到质的飞跃。

6. 总结

本文系统性地演示了如何在 Jetty 中启用并验证 HTTP/2 及 Server Push 功能:

  1. 通过模块化命令或 jetty.xml 配置 HTTP/2 连接器
  2. 正确处理 JDK 8 的 ALPN 依赖
  3. 使用 PushCacheFilter 简单粗暴地实现资源预推送
  4. 通过浏览器工具直观对比性能差异

核心价值在于减少关键资源的获取延迟。实际应用中,合理利用 Push 可显著优化首屏渲染时间。完整代码已托管至 GitHub,可直接拉取参考。

GitHub 源码:https://github.com/baeldung/tutorials/tree/master/libraries-server-2


原始标题:HTTP/2 in Jetty

» 下一篇: DBUnit 介绍