Java 11 HttpClient Http2 Слишком много потоков Ошибка - PullRequest
0 голосов
/ 28 февраля 2019

Я использую HttpClient Java 11 для отправки запроса на сервер HTTP2.Объект HttpClient создается в виде компонента Singleton Spring, как показано ниже.

@Bean
    public HttpClient getClient() {
                return HttpClient.newBuilder().version(Version.HTTP_2).executor(Executors.newFixedThreadPool(20)).followRedirects(Redirect.NORMAL)
                .connectTimeout(Duration.ofSeconds(20)).build();
    }

I am using the sendAsync method to send the requests asynchronously.

When I try to hit the server continuously, I am receiving the error after certain time "java.io.IOException: too many concurrent streams". I used Fixed threadpool in the Client building to try to overcome this error, but it is still giving the same error.

The Exception stack is..

java.util.concurrent.CompletionException: java.io.IOException: too many concurrent streams
    at java.base/java.util.concurrent.CompletableFuture.encodeRelay(CompletableFuture.java:367) ~[?:?]
    at java.base/java.util.concurrent.CompletableFuture.uniComposeStage(CompletableFuture.java:1108) ~[?:?]
    at java.base/java.util.concurrent.CompletableFuture.thenCompose(CompletableFuture.java:2235) ~[?:?]
    at java.net.http/jdk.internal.net.http.MultiExchange.responseAsyncImpl(MultiExchange.java:345) ~[java.net.http:?]
    at java.net.http/jdk.internal.net.http.MultiExchange.lambda$responseAsync0$2(MultiExchange.java:250) ~[java.net.http:?]
    at java.base/java.util.concurrent.CompletableFuture$UniCompose.tryFire(CompletableFuture.java:1072) ~[?:?]
    at java.base/java.util.concurrent.CompletableFuture.postComplete(CompletableFuture.java:506) ~[?:?]
    at java.base/java.util.concurrent.CompletableFuture$AsyncSupply.run(CompletableFuture.java:1705) ~[?:?]
    at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128) [?:?]
    at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628) [?:?]
    at java.base/java.lang.Thread.run(Thread.java:834) [?:?]
Caused by: java.io.IOException: too many concurrent streams
    at java.net.http/jdk.internal.net.http.Http2Connection.reserveStream(Http2Connection.java:440) ~[java.net.http:?]
    at java.net.http/jdk.internal.net.http.Http2ClientImpl.getConnectionFor(Http2ClientImpl.java:103) ~[java.net.http:?]
    at java.net.http/jdk.internal.net.http.ExchangeImpl.get(ExchangeImpl.java:88) ~[java.net.http:?]
    at java.net.http/jdk.internal.net.http.Exchange.establishExchange(Exchange.java:293) ~[java.net.http:?]
    at java.net.http/jdk.internal.net.http.Exchange.responseAsyncImpl0(Exchange.java:425) ~[java.net.http:?]
    at java.net.http/jdk.internal.net.http.Exchange.responseAsyncImpl(Exchange.java:330) ~[java.net.http:?]
    at java.net.http/jdk.internal.net.http.Exchange.responseAsync(Exchange.java:322) ~[java.net.http:?]
    at java.net.http/jdk.internal.net.http.MultiExchange.responseAsyncImpl(MultiExchange.java:304) ~[java.net.http:?]

Может ли кто-нибудь помочь мне в устранении этой проблемы?

Сервер - Tomcat9, и его максимальные одновременные потоки используются по умолчанию.

Ответы [ 2 ]

0 голосов
/ 05 августа 2019

К сожалению, подход с Semaphore, предложенный @sbordet, не сработал для меня.Я попробовал это:

var semaphore = semaphores.computeIfAbsent(getRequestKey(request), k -> new Semaphore(MAX_CONCURRENT_REQUESTS_NUMBER));

CompletableFuture.runAsync(semaphore::acquireUninterruptibly, WAITING_POOL)
                .thenComposeAsync(ignored -> httpClient.sendAsync(request, responseBodyHandler), ASYNC_POOL)
                .whenComplete((response, e) -> semaphore.release());

Нет никакой гарантии, что поток соединения освобождается к тому времени, когда выполнение передается следующему CompletableFuture, где освобождается семафор.Для меня подход работал в случае нормального выполнения, однако, если есть какие-либо исключения, кажется, что поток соединения может быть закрыт после вызова semaphore.release().

Наконец, я закончил с использованием OkHttp .Он решает проблему (он просто ожидает освобождения некоторых потоков, если число одновременных потоков достигает max_concurrent_streams).Он также обрабатывает кадр GOAWAY.В случае Java HttpClient мне пришлось реализовать логику повтора, чтобы справиться с этим, поскольку он просто выдает IOException, если сервер отправляет GOAWAY frame.

0 голосов
/ 28 февраля 2019

Когда я пытаюсь подключиться к серверу непрерывно

На сервере есть настройка для max_concurrent_streams, которая передается клиенту во время первоначального установления соединения HTTP / 2.

Если вы вслепую «непрерывно обращаетесь к серверу», используя sendAsync, вы не ожидаете завершения предыдущих запросов, и в итоге превышаете значение max_concurrent_streams и получаете сообщение об ошибке выше.

Решениедля одновременной отправки нескольких запросов, которые меньше max_concurrent_streams;после этого вы отправляете новый запрос только после завершения предыдущего.Это можно легко реализовать на клиенте, используя Semaphore или что-то подобное.

...