«1. Обзор

Протокол HTTP/2 поставляется с функцией push, которая позволяет серверу отправлять несколько ресурсов клиенту для одного запроса. Следовательно, это улучшает время загрузки страницы за счет сокращения количества циклов, необходимых для получения всех ресурсов.

Jetty поддерживает протокол HTTP/2 как для клиентской, так и для серверной реализации.

В этом руководстве мы рассмотрим поддержку HTTP/2 в Jetty и создадим веб-приложение Java для изучения функции HTTP/2 Push.

2. Начало работы

2.1. Загрузка Jetty

Jetty требует JDK 8 или более поздней версии и поддержку ALPN (согласование протокола прикладного уровня) для запуска HTTP/2.

Обычно сервер Jetty развертывается через SSL и включает протокол HTTP/2 через расширение TLS (ALPN).

Во-первых, нам нужно загрузить последний дистрибутив Jetty и установить переменную JETTY_HOME.

2.2. Включение коннектора HTTP/2

Далее мы можем использовать команду Java для включения коннектора HTTP/2 на сервере Jetty:

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

Эта команда добавляет поддержку протокола HTTP/2 в коннектор SSL на порту 8443. Кроме того, он транзитивно включает модуль 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

Здесь журналы показывают информацию о таких модулях, как ssl и alpn-impl/alpn-8, которые транзитивно включены для коннектора HTTP/2.

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 [email protected]{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 [email protected]{SSL, (ssl, alpn, h2)}{0.0.0.0:8443}
INFO:oejs.AbstractConnector:main: Started [email protected]{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 Server

3.1. Конфигурация Maven

Теперь, когда мы настроили Jetty, пришло время создать наше приложение.

Давайте добавим плагин Maven jetty-maven-plugin в наш pom.xml вместе с зависимостями Maven, такими как http2-server, jetty-alpn-openjdk8-server и jetty-servlets:

<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 [email protected]{HTTP/1.1, (http/1.1)}{0.0.0.0:8080}
oejs.Server:main: Started @1045ms

3.2. Настройте HTTP/2 в файле jetty.xml

Далее мы настроим сервер Jetty с протоколом HTTP/2 в нашем файле jetty.xml, добавив соответствующий элемент Call:

<?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 настроен с помощью ALPN на порту 8444 вместе с конфигурациями sslContextFactory и httpConfig.

Кроме того, мы можем добавить другие модули, такие как h2-17 и h2-16 (черновые версии h2), определив аргументы, разделенные запятыми, в jetty.xml:

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

Затем мы настроим расположение jetty.xml в нашем 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>
        <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>

Примечание. Чтобы включить HTTP/2 в нашем приложении Java 8, мы добавили jar alpn-boot в JVM BootClasspath. Однако поддержка ALPN уже доступна в Java 9 и более поздних версиях.

Давайте перекомпилируем наши классы и перезапустим приложение, чтобы проверить, включен ли протокол HTTP/2:

oejmp.Starter:main: Started Jetty Server
oejs.AbstractConnector:main: Started [email protected]{SSL, (ssl, http/1.1)}{0.0.0.0:8443}
oejs.AbstractConnector:main: Started [email protected]{SSL, (ssl, alpn, h2)}{0.0.0.0:8444}

Здесь мы видим, что порт 8443 настроен на протокол HTTP/1.1, а порт 8444 с HTTP/2.

3.3. Настройте PushCacheFilter

Далее нам нужен фильтр, который отправляет вторичные ресурсы, такие как изображения, JavaScript и CSS, клиенту.


Для этого мы можем использовать класс PushCacheFilter, доступный в пакете org.eclipse.jetty.servlets. PushCacheFilter создает кэш вторичных ресурсов, связанных с первичным ресурсом, таким как index.html, и отправляет их клиенту.

Давайте настроим PushCacheFilter в нашем web.xml:

<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. Настройте Jetty 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>

Затем мы создадим класс Http2JettyServlet для доступа к изображениям и добавим сопоставление сервлетов в наш файл web.xml:

4. Настройка Клиент HTTP/2

<!DOCTYPE html>
<html>
<head>
    <title>Baeldung 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>

Наконец, чтобы проверить функцию HTTP/2 Push и улучшить время загрузки страницы, мы создадим файл http2.html, который загружает несколько изображений (вторичные ресурсы):

5. Тестирование клиента HTTP/2

Чтобы получить базовый уровень времени загрузки страницы, давайте получим доступ к приложению HTTP/1.1 по адресу https://localhost:8443/http2.html с помощью инструментов разработчика для проверки протокола и время загрузки:

Здесь мы можем наблюдать, что изображения загружаются за 3-6 мс с использованием протокола HTTP/1.1.

Затем мы получим доступ к приложению HTTP/2 с включенной функцией Push по адресу https://localhost:8444/http2.html:

Здесь мы видим, что протокол h2, инициатором является Push, а время загрузки всех изображений (вторичных ресурсов) — 1 мс.

Таким образом, PushCacheFilter кэширует вторичные ресурсы для http2.html, отправляет их на порт 8444 и значительно сокращает время загрузки страницы.

6. Заключение

В этом руководстве мы изучили HTTP/2 в Jetty.

Сначала мы рассмотрели, как запустить Jetty с протоколом HTTP/2 вместе с его настройками.

Затем мы увидели веб-приложение Java 8 с функцией HTTP/2 Push, настроенное с помощью PushCacheFilter, и наблюдали, как время загрузки страницы, содержащей вторичные ресурсы, улучшилось по сравнению с тем, что мы видели с протоколом HTTP/1.1. .

Как обычно, все реализации кода доступны на GitHub.

«