study-http2

0

Описание

Проект для изучения HTTP/2, используя Spring Boot приложения

Языки

  • Java95%
  • Shell5%
readme.md

Проект для изучения HTTP/2, используя Spring Boot приложения

Приложение 1, использующее HTTP/1.1

В приложении на Tomcat-е поднят HTTP/1.1 endpoint, который закрыт mTLS-ом (порт 8443).
При попытке получить сертификаты сервера, он выдаёт свой серверный сертификат и корневой, которым подписан:

openssl s_client -showcerts -connect localhost:8443

Обратиться к endpoint-у можно так:

cd ${projectRoot}/certificates curl https://localhost:8443/http1-app/echo -v \ --cacert root/root-ca.pem --cert client/client.pem --key client/client.key --pass client \ --request POST --header "Content-Type: application/json" --data '{"data": "requestData"}'

В многословном ответе

curl
-а будет следующее подтверждение использования протокола HTTP/1.1:

* ALPN, server did not agree to a protocol ... > POST /http1-app/echo HTTP/1.1

Приложение 2, использующее HTTP/2

В приложении на Tomcat-е поднят HTTP/2 endpoint, который закрыт mTLS-ом (порт 9443).
При попытке получить сертификаты сервера, он выдаёт свой серверный сертификат и корневой, которым подписан:

openssl s_client -showcerts -connect localhost:9443

Обратиться к endpoint-у можно так:

cd ${projectRoot}/certificates curl https://localhost:9443/http2-app/echo -v \ --cacert root/root-ca.pem --cert client/client.pem --key client/client.key --pass client \ --request POST --header "Content-Type: application/json" --data '{"data": "requestData"}'

В многословном ответе

curl
-а будет следующее подтверждение использования протокола HTTP/2:

* ALPN, server accepted to use h2 * ... * Using HTTP2, server supports multiplexing ... > POST /http2-app/echo HTTP/2

Выводы

  1. Достаточно добавить в

    application.yml
    строку:
    server.http2.enabled=true

    и Tomcat поднимет endpoint, используя протокол HTTP/2.
    См. https://stackoverflow.com/questions/38612704/enable-http2-with-tomcat-in-spring-boot,
    https://www.baeldung.com/spring-boot-http2-tomcat.

  2. Браузер по умолчанию держит несколько соединений для HTTP/1.1 и одно соединение для HTTP/2.
    Нужно отправить из браузера GET-запрос

    https://localhost:8443/http1-app/echo?data=requestData

    или
    https://localhost:9443/http2-app/echo?data=requestData
    , проверяя установленные соединения командами:

    (i) По умолчанию браузер держит эти соединения 1 минуту, после чего обрывает, если нет других запросов.
    (i) Чтобы из браузера доходили запросы, нужно закомментировать в

    application.yml
    строку
    client-auth: need
    (т.е. переключиться с mTLS на односторонний TLS), а также добавить в доверенные сертификаты браузера
    ${projectRoot}/certificates/root/root-ca.pem
    .

  3. Curl закрывает соединения после каждого запроса (и для HTTP/1.1, и для HTTP/2).

  4. Время жизни соединения HTTP/2 может быть ограничено только сервером.
    Для этого через бин

    TomcatConnectorCustomizer
    можно найти объект
    Http2Protocol
    методом
    Connector.findUpgradeProtocols()
    и вызвать у него метод
    setKeepAliveTimeout( <value in millis> )
    .
    В Apache HttpClient v5 не удалось найти способ ограничить keepAlive или timeToLive (ни через
    ConnectionConfig
    , ни через
    RequestConfig
    , ни через
    IOReactorConfig
    , ни через
    evictIdleConnections()
    ). Если сервер жив и отвечает, то клиент не прерывает даже idle соединение.
    (!) Единственный способ закрыть HTTP/2 соединение на клиенте - закрыть экземпляр HttpClient-а.

  5. Выводы по экспериментам с запросами/ответами в 1 КБ (+ 300 Б заголовков):

    • На HTTP/2 без pool-а:
      1. latency в 6,8 раза меньше, чем на HTTP/1.1
      2. throughput в 12,7 раза больше, чем на HTTP/1.1 (утилизация CPU ~ равна 50%, на HTTP/2 была даже меньше, т.к. не удалось загрузить сильнее)
    • На HTTP/2 с pool-ом соединений из библиотеки Apache HttpClient v5:
      1. latency такая же, как на HTTP/1.1
      2. throughput в 2,9 раза больше, чем на HTTP/1.1 (утилизация CPU ~ равна 50%)
    • На HTTP/2 с собственным pool-ом экземпляров HttpClient-а (таких, как в HTTP/2 без pool-а):
      1. latency в 6,8 раза меньше, чем на HTTP/1.1
      2. throughput в 22,9 раза больше, чем на HTTP/1.1 (утилизация CPU ~ равна 50%)
    • Последний вариант использования HTTP/2 самый эффективный.
  6. Выводы по экспериментам с запросами/ответами в 1 МБ (+ 300 Б заголовков):

    • На HTTP/2 без pool-а:
      1. latency на 9,5% меньше, чем на HTTP/1.1
      2. (!) throughput в 2,4 раза меньше, чем на HTTP/1.1 (утилизация CPU ~ равна 50%, на HTTP/2 была сильно меньше, т.к. не удалось загрузить сильнее)
    • На HTTP/2 с pool-ом соединений из библиотеки Apache HttpClient v5:
      1. latency на 1,6% меньше, чем HTTP/1.1
      2. throughput на 0,8% больше, чем на HTTP/1.1 (утилизация CPU ~ равна 50%)
    • На HTTP/2 с собственным pool-ом экземпляров HttpClient-а (таких, как в HTTP/2 без pool-а):
      1. latency на 6,6% меньше, чем на HTTP/1.1
      2. (!) throughput на 11,2% больше, чем на HTTP/1.1 (утилизация CPU ~ равна 50%)
    • Последний вариант использования HTTP/2 самый эффективный.
    • Вывод о сравнении с запросами/ответами в 1 КБ:
      На мегабайтных запросах/ответах у HTTP/2 хуже пропускная способность, чем у HTTP/1.1, используя connection pool.
      Это не удивительно, поскольку клиент HTTP/2 использует только одно TCP-соединение, куда куча параллельных мегабайтов не влезает.
      Пришлось помочь HTTP/2 закрыть этот недостаток, реализовав собственный pool экземпляров клиентов HTTP/2 (чтобы каждый поток брал только свободного клиента, либо создавал нового).
      Использование HTTP/2 с таким custom clients pool-ом не только убрало проблему с пропускной способностью на 1 МБ, но и улучшило этот показатель на 1 КБ (не ухудшив остальное).