Статьи

Введение в поддержку HTTP / 2 в Java 9

1. Введение

Потоковая группа IETF одобрила протокол HTTP / 2 в 2015 году, через шестнадцать лет после выпуска HTTP / 1.1. HTTP / 2 обещает снизить задержку и делает многие из тех обходных путей устаревшими, которые были необходимы для HTTP / 1.1, чтобы соответствовать современным требованиям времени отклика. В этой статье я кратко расскажу о HTTP / 2 и о том, как он обновляет текстовый HTTP / 1.1, а затем мы рассмотрим будущую поддержку HTTP / 2 в Java 9.

2. Методы оптимизации задержки для HTTP / 1.1

Люди становятся все более и более стационарными в Интернете, но они не заметят, что действия, которые они выполняют в Интернете, не выполняются сами по себе, если время ответа меньше 100 мс.

Когда время отклика увеличивается до 1 с, что замечается, и когда сайт отвечает дольше 10 с, он считается неработоспособным. Согласно некоторым исследованиям, средняя продолжительность внимания снизилась до 7-8 секунд, и даже задержка в 1 секунду может привести к потере дохода на 7%.

Для HTTP / 1.1 требовались (иногда тяжелые) обходные пути для удовлетворения сегодняшних требований.

  • Поскольку одно HTTP-соединение может загружать по одному ресурсу за раз, браузеры извлекают их одновременно, чтобы быстрее отображать страницу. Однако число параллельных подключений на домен ограничено, и для решения этой проблемы было использовано разбиение домена .
  • Подобный метод оптимизации заключался в объединении нескольких ресурсов (CSS, JavaScript) в один пакет, чтобы можно было получить их одним запросом. Компромисс избавляет от необходимости обходить сеть с риском не использовать некоторые части собранного комплекта ресурсов вообще. В некоторых случаях сложная логика на стороне сервера заботится о выборе подходящих статических ресурсов и объединении их для конкретного запроса страницы
  • Спрайты изображений — это метод, аналогичный пакетированию файлов CSS и JavaScript, для уменьшения количества запросов.
  • Другой метод заключается во встраивании статических ресурсов в HTML.

3. Краткое введение в HTTP / 2

HTTP / 2 предназначен для облегчения боли, связанной с поддержанием сложной инфраструктуры для HTTP / 1.1, для обеспечения его эффективной работы. Хотя HTTP / 2 по-прежнему обратно совместим с HTTP / 1.1, он больше не является текстовым протоколом. Клиенты устанавливают соединение как HTTP / 1.1-запрос, а также запросы и обновления. С этого момента HTTP / 2 говорит в двоичных фреймах данных.

3.1. HTTP / 2 мультиплексирование

Благодаря мультиплексированию HTTP / 2 все обходные пути HTTP / 1.1, описанные выше, устарели, поскольку одно соединение может обрабатывать несколько двунаправленных потоков, что позволяет клиентам загружать несколько ресурсов по одному соединению одновременно.

3.2. Сжатие заголовка HTTP / 2

Протоколы HTTP 1.x были основаны на тексте и, следовательно, были многословны. Иногда один и тот же набор заголовков HTTP обменивался снова и снова. HTTP / 2 резко снижает требуемую пропускную способность, поддерживая таблицу заголовков HTTP между запросами. По сути, это дедупликация, а не сжатие в классическом смысле.

3.3. HTTP / 2 push

Вы можете подумать, что HTTP / 2 push — это продолжение или какое-то обновление до WebSocket, но это не так. Хотя WebSocket является средством полнодуплексной связи между клиентом и сервером, чтобы сервер мог отправлять данные клиентам после установления TCP-соединения, HTTP / 2 решает отдельную проблему.

HTTP / 2 push — это проактивная отправка ресурсов клиентам без необходимости запрашивать их с точки зрения клиента. Это практически означает, что серверная сторона знает, что веб-сайту нужны некоторые изображения, и отправляет их все сразу (заранее), задолго до того, как клиенты их запросят.

4. Java HTTP клиенты, поддерживающие HTTP / 2

Согласно одной из страниц Wiki HTTP / 2, на момент написания доступны следующие клиентские библиотеки Java для установления соединений HTTP / 2.

  • пристань
  • Нетти
  • OkHttp
  • Vert.x
  • Светляк

Однако в этой статье мы сконцентрируемся на поддержке HTTP / 2, предоставляемой Java 9. JEP 110 определяет требования, а также заявляет, что проект все еще находится в состоянии инкубации, что практически означает, что он не заменит существующую UrlConnection. API в Java 9.

Только с Java 10 стандартный клиент Java HTTP / 2 будет перемещен в пакет java.net . Тем временем, однако, он будет жить в пространстве имен jdk.incubtor .

5. Изучите HTTP / 2 клиент Java 9

JEP 110 устанавливает требования для нового встроенного клиента HTTP / 2, чтобы обеспечить высокий уровень, простой в использовании API и сопоставимую (или более высокую) производительность, чем существующие альтернативы (см. Выше).

Первым шагом является импорт модуля jdk.incubator.httpclient .

1
2
3
module com.springui.echo.client {
  requires jdk.incubator.httpclient;
}

В этом примере мы будем использовать Undertow в качестве HTTP / 2-совместимого веб-сервера. Он просто возвращает то сообщение, которое отправляют ему клиенты.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
public class EchoServer {
 
  private static final Logger LOGGER = Logger.getLogger(EchoServer.class.getSimpleName());
 
  private static final int PORT = 8888;
  private static final String HOST = "localhost";
 
  public static void main(final String[] args) {
    Undertow server = Undertow.builder()
        .setServerOption(UndertowOptions.ENABLE_HTTP2, true)
        .addHttpListener(PORT, HOST)
        .setHandler(exchange -> {
          LOGGER.info("Client address is: " + exchange.getConnection().getPeerAddress().toString());
          exchange.getResponseHeaders().put(Headers.CONTENT_TYPE, "text/plain");
          exchange.getRequestReceiver().receiveFullString((e, m) -> e.getResponseSender().send(m));
        }).build();
 
    server.start();
  }
 
}

Новый API везде следует шаблону компоновщика , и HttpClient , являющийся точкой входа в инициирование HTTP-запросов, не является исключением из этого.

1
2
3
4
HttpClient client = HttpClient
    .newBuilder()
    .version(Version.HTTP_2)
    .build();

5.1. Отправка запроса в режиме блокировки

Если у нас есть экземпляр HttpClient, экземпляры HttpRequest также можно создавать с помощью компоновщика.

1
2
3
4
5
6
7
HttpResponse<String> response = client.send(
    HttpRequest
        .newBuilder(TEST_URI)
        .POST(BodyProcessor.fromString("Hello world"))
        .build(),
    BodyHandler.asString()
);

Метод отправки блока до тех пор, пока обрабатывается запрос, однако есть и способ асинхронного обмена HTTP-сообщениями.

5.2. Отправка запросов в неблокирующем режиме

В следующем примере 10 случайных целых чисел отправляются на наш HTTP-эхо-сервер асинхронно, и когда все запросы были инициированы, основной поток ожидает их завершения.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
List<CompletableFuture<String>> responseFutures = new Random()
    .ints(10)
    .mapToObj(String::valueOf)
    .map(message -> client
        .sendAsync(
          HttpRequest.newBuilder(TEST_URI)
            .POST(BodyProcessor.fromString(message))
            .build(),
          BodyHandler.asString()
        )
        .thenApply(r -> r.body())
    )
    .collect(Collectors.toList());
 
CompletableFuture.allOf(responseFutures.toArray(new CompletableFuture<?>[0])).join();
 
responseFutures.stream().forEach(future -> {
  LOGGER.info("Async response: " + future.getNow(null));
});

5.3. Обработка кадров с обещаниями

Все приведенные выше примеры могли бы быть обычными, устаревшими запросами HTTP / 1.1. Помимо создания HttpClient , ничего специфичного для HTTP / 2 не наблюдается.

Вероятно, наиболее важной функцией HTTP / 2 в клиентском API является способ обработки нескольких ответов при использовании HTTP / 2 push.

01
02
03
04
05
06
07
08
09
10
11
Map<HttpRequest, CompletableFuture<HttpResponse<String>>> responses =
  client.sendAsync(
    HttpRequest.newBuilder(TEST_URI)
      .POST(BodyProcessor.fromString(TEST_MESSAGE))
      .build(),
    MultiProcessor.asMap(request -> Optional.of(BodyHandler.asString()))
  ).join();
 
responses.forEach((request, responseFuture) -> {
  LOGGER.info("Async response: " + responseFuture.getNow(null));
});

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

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

С точки зрения Java 9 новый клиент HTTP / 2 выглядит красиво, однако он будет готов к работе только в следующем выпуске. Тем временем вышеупомянутые библиотеки могут быть использованы, если требуется поддержка HTTP / 2.

Обновление: HTTP-клиент JEP 312 предлагает стандартизировать API-интерфейс HTTP-клиента, который был представлен в качестве инкубационного API-интерфейса в Java 9 и обновлен в Java 10. Начиная с Java 11 это полноценная функция модуля java.net .

Если вы хотите узнать больше о Java 9, вы также можете проверить эти учебники по Java 9 от Java Code Geeks.

См. Оригинальную статью здесь: Введение в поддержку HTTP / 2 в Java 9

Мнения, высказанные участниками Java Code Geeks, являются их собственными.