Jetty中的HTTP/2

2025/03/28

1. 概述

HTTP/2协议带有推送功能,允许服务器通过单个请求将多个资源发送到客户端。因此,它通过减少获取所有资源所需的多次往返来改善页面的加载时间。

Jetty支持客户端和服务器实现的HTTP/2协议。

在本教程中,我们将探索Jetty中的HTTP/2支持并创建一个Java Web应用程序来演示HTTP/2推送功能

2. 入门

2.1 下载Jetty

Jetty需要JDK 8或更高版本以及ALPN(应用层协议协商)支持才能运行HTTP/2。

通常,Jetty服务器通过SSL部署,并通过TLS扩展(ALPN)启用HTTP/2协议

首先,我们需要下载最新的Jetty发行版并设置JETTY_HOME变量。

2.2 启用HTTP/2 Connector

接下来,我们可以使用Java命令在Jetty服务器上启用HTTP/2连接器:

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

此命令为端口8443上的SSL连接器添加HTTP/2协议支持。此外,它还过渡启用ALPN模块进行协议协商:

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  : alpn-impl/alpn-8 dynamic dependency of alpn-impl
INFO  : http2           initialized in ${jetty.base}/start.ini
INFO  : ssl             transitively enabled, ini template available with --add-to-start=ssl
INFO  : threadpool      transitively enabled, ini template available with --add-to-start=threadpool
INFO  : bytebufferpool  transitively enabled, ini template available with --add-to-start=bytebufferpool
INFO  : Base directory was modified

这里,日志显示了为HTTP/2连接器过渡启用的ssl和alpn-impl/alpn-8等模块的信息。

2.3 启动Jetty服务器

现在,我们准备启动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

2.4 启用附加模块

类似地,我们可以启用其他模块,如http和http2c:

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}
INFO:oejs.Server:main: Started @685ms

另外,我们可以列出Jetty提供的所有模块:

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

输出将如下所示:

Available Modules:
==================
tags: [-internal]
Modules for tag '*':
--------------------
     Module: alpn 
           : Enables the ALPN (Application Layer Protocol Negotiation) TLS extension.
     Depend: ssl, alpn-impl
        LIB: lib/jetty-alpn-client-${jetty.version}.jar
        LIB: lib/jetty-alpn-server-${jetty.version}.jar
        XML: etc/jetty-alpn.xml
    Enabled: transitive provider of alpn for http2
    // ...

Modules for tag 'connector':
----------------------------
     Module: http2 
           : Enables HTTP2 protocol support on the TLS(SSL) Connector,
           : using the ALPN extension to select which protocol to use.
       Tags: connector, http2, http, ssl
     Depend: ssl, alpn
        LIB: lib/http2/*.jar
        XML: etc/jetty-http2.xml
    Enabled: ${jetty.base}/start.ini
    // ...

Enabled Modules:
================
    0) alpn-impl/alpn-8 dynamic dependency of alpn-impl
    1) http2           ${jetty.base}/start.ini
    // ...

2.5 附加配置

与–list-modules参数类似,我们可以使用–list-config列出每个模块的所有XML配置文件:

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

要配置Jetty服务器的主机和端口等常用属性,我们可以在start.ini文件中进行更改:

jetty.ssl.host=0.0.0.0
jetty.ssl.port=8443
jetty.ssl.idleTimeout=30000

此外,我们还可以配置一些http2属性,例如maxConcurrentStreams和maxSettingsKeys:

jetty.http2.maxConcurrentStreams=128
jetty.http2.initialStreamRecvWindow=524288
jetty.http2.initialSessionRecvWindow=1048576
jetty.http2.maxSettingsKeys=64
jetty.http2.rateControl.maxEventsPerSecond=20

3. 设置Jetty服务器应用程序

3.1 Maven配置

现在我们已经配置了Jetty,是时候创建我们的应用程序了。

我们将jetty-maven-plugin Maven插件以及Maven依赖(如http2-serverjetty-alpn-openjdk8-serverjetty-servlets)添加到我们的pom.xml中:

<build>
    <plugins>
        <plugin>
            <groupId>org.eclipse.jetty</groupId>
            <artifactId>jetty-maven-plugin</artifactId>
            <version>9.4.27.v20200227</version>
            <dependencies>
                <dependency>
                    <groupId>org.eclipse.jetty.http2</groupId>
                    <artifactId>http2-server</artifactId>
                    <version>9.4.27.v20200227</version>
                </dependency>
                <dependency>
                    <groupId>org.eclipse.jetty</groupId>
                    <artifactId>jetty-alpn-openjdk8-server</artifactId>
                    <version>9.4.27.v20200227</version>
                </dependency>
                <dependency>
                    <groupId>org.eclipse.jetty</groupId>
                    <artifactId>jetty-servlets</artifactId>
                    <version>9.4.27.v20200227</version>
                </dependency>
            </dependencies>
        </plugin>
    </plugins>
</build>

然后,我们将使用Maven命令编译类:

mvn clean package

最后,我们可以将未组装的Maven应用程序部署到Jetty服务器:

mvn jetty:run-forked

默认情况下,服务器从端口8080启动,使用HTTP/1.1协议:

oejmp.Starter:main: Started Jetty Server
oejs.AbstractConnector:main: Started ServerConnector@4d910fd6{HTTP/1.1, (http/1.1)}{0.0.0.0:8080}
oejs.Server:main: Started @1045ms

3.2 在jetty.xml中配置HTTP/2

接下来,我们将在jetty.xml文件中添加适当的Call元素,使用HTTP/2协议配置Jetty服务器:

<?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 and httpConfig configs-->

    <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">
                        <Item>
                            <New class="org.eclipse.jetty.server.SslConnectionFactory">
                                <Arg name="sslContextFactory"><Ref id="sslContextFactory"/></Arg>
                                <Arg name="next">alpn</Arg>
                            </New>
                        </Item>
                        <Item>
                            <New class="org.eclipse.jetty.alpn.server.ALPNServerConnectionFactory">
                                <Arg>h2</Arg>
                            </New>
                        </Item>
                        <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>

    <!-- other Call elements -->
</Configure>

这里,HTTP/2连接器在端口8444上配置了ALPN以及sslContextFactory和httpConfig配置。

另外,我们可以通过在jetty.xml中定义逗号分隔的参数来添加其他模块,如h2-17和h2-16(h2的草稿版本):

<Item> 
    <New class="org.eclipse.jetty.alpn.server.ALPNServerConnectionFactory"> 
        <Arg>h2,h2-17,h2-16</Arg> 
    </New> 
</Item>

然后,我们将在pom.xml中配置jetty.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>
        <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>

注意:为了在我们的Java 8应用中启用HTTP/2,我们已将alpn-boot jar添加到JVM BootClasspath。但是,Java 9或更高版本已提供ALPN支持

让我们重新编译我们的类并重新运行应用程序来验证HTTP/2协议是否启用:

oejmp.Starter:main: Started Jetty Server
oejs.AbstractConnector:main: Started ServerConnector@6fadae5d{SSL, (ssl, http/1.1)}{0.0.0.0:8443}
oejs.AbstractConnector:main: Started ServerConnector@1810399e{SSL, (ssl, alpn, h2)}{0.0.0.0:8444}

在这里我们可以观察到端口8443配置了HTTP/1.1协议,而8444配置了HTTP/2。

3.3 配置PushCacheFilter

接下来,我们需要一个过滤器,将图像、JavaScript和CSS等次要资源推送到客户端。

为此,我们可以使用org.eclipse.jetty.servlets包中的PushCacheFilter类。PushCacheFilter会构建与主要资源(如index.html)关联的次要资源的缓存,并将其推送到客户端。

让我们在web.xml中配置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>
    </init-param>
</filter>
<filter-mapping>
    <filter-name>push</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

3.4 配置JettyServlet和Servlet映射

然后,我们将创建Http2JettyServlet类来访问图像,并在我们的web.xml文件中添加servlet-mapping:

<servlet>
    <servlet-name>http2Jetty</servlet-name>
    <servlet-class>cn.tuyucheng.taketoday.jetty.http2.Http2JettyServlet</servlet-class>
</servlet>
<servlet-mapping>
    <servlet-name>http2Jetty</servlet-name>
    <url-pattern>/images/*</url-pattern>
</servlet-mapping>

4. 设置HTTP/2客户端

最后,为了验证HTTP/2推送功能和改进的页面加载时间,我们将创建一个加载一些图像(辅助资源)的http2.html文件:

<!DOCTYPE html>
<html>
<head>
    <title>Tuyucheng HTTP/2 Client in Jetty</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/2客户端

为了获取页面加载时间的基准,让我们使用开发人员工具访问https://localhost:8443/http2.html上的HTTP/1.1应用程序来验证协议和加载时间:

在这里,我们可以观察到使用HTTP/1.1协议图像在3-6ms内加载。

然后,我们将通过https://localhost:8444/http2.html访问已启用推送的HTTP/2应用程序:

这里我们观察到协议是h2,发起者是Push,所有图片(辅助资源)的加载时间都是1ms。

因此,PushCacheFilter会缓存http2.html的二级资源,并将其推送到8444端口,大大提高页面的加载时间。

6. 总结

在本教程中,我们探索了Jetty中的HTTP/2。

首先,我们研究了如何使用HTTP/2协议启动Jetty及其配置。

然后,我们构建了一个具有HTTP/2 Push特性的Java 8 Web应用程序,它配置了PushCacheFilter,并观察了包含辅助资源的页面的加载时间与使用HTTP/1.1协议相比有何改进。

Show Disqus Comments

Post Directory

扫码关注公众号:Taketoday
发送 290992
即可立即永久解锁本站全部文章