как использовать функцию мультиплексирования http2 при загрузке - PullRequest
0 голосов
/ 06 февраля 2020

Должно быть значительное улучшение производительности при использовании функции мультиплексирования http2 при загрузке нескольких файлов.

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

Эта задача, кажется, не простая, как я думал изначально, или, с другой стороны, кажется, что я не смог найти сервер, способный использовать мультиплексирование при загрузке (если существует).

Это код, который я написал, у кого-нибудь есть мысли?

HttpClient httpClient = HttpClient.newBuilder().version(HttpClient.Version.HTTP_2).build();
String url = "https://your-own-http2-server.com/incoming-files/%s";
Path basePath = Path.of("/path/to/directory/where/is/a/bunch/of/jpgs");

Function<Path, CompletableFuture<HttpResponse<String>>> handleFile = file -> {
    String currentUrl = String.format(url, file.getFileName().toString());
    try {
        HttpRequest request = HttpRequest.newBuilder()
                                         .uri(URI.create(currentUrl))
                                         .header("Content-Type", "image/jpeg")
                                         .PUT(HttpRequest.BodyPublishers.ofFile(file))
                                         .build();
        return httpClient.sendAsync(request, HttpResponse.BodyHandlers.ofString());
    } catch (IOException e) {
        e.printStackTrace();
        throw new RuntimeException(e);
    }
};

List<Path> files = Files.list(basePath).collect(toList());

files.parallelStream().map(handleFile).forEach(c -> {
         try {
             final HttpResponse<String> response = c.get();
             System.out.println(response.statusCode());
         } catch (Exception e) {
             e.printStackTrace();
             throw new RuntimeException((e));
         }
     });

Ответы [ 2 ]

3 голосов
/ 06 февраля 2020

Должно быть значительное улучшение производительности при использовании функции мультиплексирования http2 при загрузке нескольких файлов.

Это предположение, как правило, неверное.

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

Затем у нас будет 1 TCP-соединение, и мы хотим сравнить загрузку с HTTP / 1.1 и HTTP / 2.

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

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

В HTTP / 1.1 у вас будет upload1.start...upload1.end|upload2.start...upload2.end|upload3.start...upload3.end et c .

В HTTP / 2 у вас будет upload1.start|upload2.start|upload3.start.....upload3.end..upload1.end..upload2.end

Время окончания будет таким же.

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

В спецификации HTTP / 2 по умолчанию используется управление потоком HTTP / 2 окно в 65535 байтах, что означает, что каждые 65535 байтов клиент должен прекратить отправку данных, пока сервер не подтвердит эти байты. Это может занять поездку туда и обратно, поэтому, даже если поездка туда и обратно мала (например, 50 мс) для больших загрузок файлов, вы можете оплачивать эту поездку несколько раз, добавляя секунды к своим загрузкам (например, для загрузки 6 МБ вы можете заплатить эту стоимость 100). время, что составляет 5 секунд).

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

При большем управлении потоком HTTP / 2 windows сервер может быть умным и отправлять подтверждения клиенту, пока клиент все еще загружает.

Когда клиент загружает, это уменьшает его " отправить "окно. Получая подтверждения от сервера, клиент расширяет окно «send».

Типичное плохое взаимодействие - указание клиенту значения окна «send», начиная с 1 МиБ:

[client send window]

1048576 
        client sends 262144 bytes
786432  
        client sends 262144 bytes
524288  
        client sends 262144 bytes
262144  
        client sends 262144 bytes
0       
        client cannot send
.
. (stalled)
.
        client receives acknowledgment from server (524288 bytes)
524288  
        client sends 262144 bytes
262144  
        client sends 262144 bytes
0       
        client cannot send
.
. (stalled)
.

Хорошее взаимодействие будет выглядеть следующим образом:

[client send window]

1048576 
        client sends 262144 bytes
786432  
        client sends 262144 bytes
524288  
        client sends 262144 bytes
262144  
        client receives acknowledgment from server (524288 bytes)
786432  
        client sends 262144 bytes
524288  
        client sends 262144 bytes
262144  
        client receives acknowledgment from server (524288 bytes)
786432  

Как видно из хорошего взаимодействия, сервер подтверждает клиент до того, как клиент исчерпает окно «отправить», поэтому клиент может продолжать отправку в полном объеме. скорость.

Мультиплексирование действительно эффективно для многих небольших запросов, что является примером использования браузера: множество небольших запросов GET (без содержимого запроса), которые могут быть мультиплексированы в HTTP / 2, поступая на сервер раньше, чем соответствующие запросы HTTP / 1.1, и, как таковые, будут обрабатываться раньше и возвращаться в браузер раньше.

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

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

Браузеры избегают этой проблемы, говоря серверу, что окно «отправки» сервера действительно очень большое - Firefox 72 устанавливает его равным 12 МБ на соединение и очень умно распознает сервер, чтобы он не останавливал загрузки.

0 голосов
/ 06 февраля 2020

java.net.http.HttpClient обрабатывает байты, передаваемые через BodyPublisher, как необработанные данные тела, без какой-либо интерпретации. Чтобы проиллюстрировать мою точку зрения, семантически не имеет значения, используете ли вы HttpRequest.BodyPublishers::ofFile(Path) или HttpRequest.BodyPublishers::ofByteArray(byte[]): что просто меняет способ получения байтов, которые будут переданы удаленной стороне. В случае загрузки файла - сервер, вероятно, ожидает, что тело запроса будет отформатировано определенным образом. Можно также ожидать, что некоторые указанные c заголовки будут переданы вместе с запросом (например, Content-Type et c). HttpClient не сделает это волшебным образом для вас. На данный момент это то, что не предлагается из коробки API. Вы должны будете реализовать это на уровне вызывающего абонента. (Имеется протокол RFE для исследования поддержки multipart / form-data, но он еще не сделал это в API https://bugs.openjdk.java.net/browse/JDK-8235761).

...