Асинхронный HTTP с асинхронным http-клиентом в Java

Асинхронный HTTP с async-http-клиентом в Java

1. обзор

AsyncHttpClient (AHC) - это библиотека, построенная на основеNetty, с целью простого выполнения HTTP-запросов и асинхронной обработки ответов.

В этой статье мы расскажем, как настроить и использовать HTTP-клиент, как выполнить запрос и обработать ответ с помощью AHC.

2. Настроить

Последнюю версию библиотеки можно найти в папкеMaven repository. Мы должны быть осторожны, чтобы использовать зависимость с идентификатором группыorg.asynchttpclient, а не с зависимостью сcom.ning:


    org.asynchttpclient
    async-http-client
    2.2.0

3. Конфигурация клиента HTTP

Самый простой способ получить HTTP-клиент - использовать классDsl. Статический методasyncHttpClient() возвращает объектAsyncHttpClient:

AsyncHttpClient client = Dsl.asyncHttpClient();

Если нам нужна специальная конфигурация HTTP-клиента, мы можем построить объектAsyncHttpClient с помощью построителяDefaultAsyncHttpClientConfig.Builder:

DefaultAsyncHttpClientConfig.Builder clientBuilder = Dsl.config()

Это дает возможность настроить тайм-ауты, прокси-сервер, HTTP-сертификаты и многое другое:

DefaultAsyncHttpClientConfig.Builder clientBuilder = Dsl.config()
  .setConnectTimeout(500)
  .setProxyServer(new ProxyServer(...));
AsyncHttpClient client = Dsl.asyncHttpClient(clientBuilder);

После того, как мы настроили и получилиan instance of the HTTP client we can reuse it across out application. Нам не нужно создавать экземпляр для каждого запроса, потому что внутри он создает новые потоки и пулы соединений, что приведет к проблемам с производительностью.

Также важно отметить, чтоonce we’ve finished using the client we should call to close() method to prevent any memory leaks или зависшие ресурсы.

4. Создание HTTP-запроса

Есть два метода, в которых мы можем определить HTTP-запрос, используя AHC:

  • связанный

  • несвязанный

Нет большой разницы между двумя типами запросов с точки зрения производительности. Они представляют только два отдельных API, которые мы можем использовать для определения запроса. A bound request is tied to the HTTP client it was created from and will, by default, use the configuration of that specific client if not specified otherwise.с

Например, при создании связанного запроса флагdisableUrlEncoding считывается из конфигурации HTTP-клиента, а для несвязанного запроса по умолчанию установлено значение false. Это полезно, потому что конфигурацию клиента можно изменить без перекомпиляции всего приложения, используя системные свойства, передаваемые в качестве аргументов виртуальной машины:

java -jar -Dorg.asynchttpclient.disableUrlEncodingForBoundRequests=true

Полный список свойств можно найти в файлеahc-default.properties.

4.1. Связанный запрос

Для создания связанного запроса мы используем вспомогательные методы из классаAsyncHttpClient, которые начинаются с префикса“prepare”. Также мы можем использовать методprepareRequest(), который получает уже созданный объектRequest.

Например, методprepareGet() создаст HTTP-запрос GET:

BoundRequestBuilder getRequest = client.prepareGet("http://www.example.com");

4.2. Несвязанный запрос

Несвязанный запрос можно создать с помощью классаRequestBuilder:

Request getRequest = new RequestBuilder(HttpConstants.Methods.GET)
  .setUrl("http://www.example.com")
  .build();

или с помощью вспомогательного классаDsl, который фактически используетRequestBuilder для настройки метода HTTP и URL-адреса запроса:

Request getRequest = Dsl.get("http://www.example.com").build()

5. Выполнение HTTP-запросов

Название библиотеки дает нам подсказку о том, как запросы могут быть выполнены. AHC поддерживает как синхронные, так и асинхронные запросы.

Выполнение запроса зависит от его типа. При использовании классаbound request we use the execute() method from the BoundRequestBuilder и когда у нас естьunbound request we’ll execute it using one of the implementations of the executeRequest() method from the AsyncHttpClient interface.

5.1. Синхронно

Библиотека была спроектирована как асинхронная, но при необходимости мы можем имитировать синхронные вызовы, заблокировав объектFuture. Оба методаexecute() иexecuteRequest() возвращают объектListenableFuture<Response>. Этот класс расширяет интерфейс JavaFuture, наследуя методget(), который можно использовать для блокировки текущего потока до тех пор, пока HTTP-запрос не будет завершен и не вернет ответ:

Future responseFuture = boundGetRequest.execute();
responseFuture.get();
Future responseFuture = client.executeRequest(unboundRequest);
responseFuture.get();

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

5.2. Асинхронный

Когда мы говорим об асинхронных выполнениях, мы также говорим о слушателях для обработки результатов. Библиотека AHC предоставляет 3 типа слушателей, которые можно использовать для асинхронных HTTP-вызовов:

  • AsyncHandler

  • AsyncCompletionHandler

  • ListenableFuture слушателей

СлушательAsyncHandler предлагает возможность контролировать и обрабатывать HTTP-вызов до его завершения. С его помощью можно обработать серию событий, связанных с вызовом HTTP:

request.execute(new AsyncHandler() {
    @Override
    public State onStatusReceived(HttpResponseStatus responseStatus)
      throws Exception {
        return null;
    }

    @Override
    public State onHeadersReceived(HttpHeaders headers)
      throws Exception {
        return null;
    }

    @Override
    public State onBodyPartReceived(HttpResponseBodyPart bodyPart)
      throws Exception {
        return null;
    }

    @Override
    public void onThrowable(Throwable t) {

    }

    @Override
    public Object onCompleted() throws Exception {
        return null;
    }
});


ПеречислениеState позволяет нам контролировать обработку HTTP-запроса. ВозвращаяState.ABORT we can stop the processing в определенный момент и используяState.CONTINUE, мы даем завершение обработки.

Важно отметить, чтоAsyncHandler isn’t thread-safe and shouldn’t be reused when executing concurrent requests.

AsyncCompletionHandler наследует все методы интерфейсаAsyncHandler и добавляет вспомогательный методonCompleted(Response) для обработки завершения вызова. Все другие методы прослушивателя переопределяются для возвратаState.CONTINUE, что делает код более читаемым:

request.execute(new AsyncCompletionHandler() {
    @Override
    public Object onCompleted(Response response) throws Exception {
        return response;
    }
});


ИнтерфейсListenableFuture позволяет нам добавлять слушателей, которые будут запускаться после завершения HTTP-вызова.

Кроме того, давайте выполним код от слушателей - используя другой пул потоков:

ListenableFuture listenableFuture = client
  .executeRequest(unboundRequest);
listenableFuture.addListener(() -> {
    Response response = listenableFuture.get();
    LOG.debug(response.getStatusCode());
}, Executors.newCachedThreadPool());

Помимо возможности добавления слушателей, интерфейсListenableFuture позволяет нам преобразовать ответFuture вCompletableFuture.

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

AHC - очень мощная библиотека с множеством интересных функций. Он предлагает очень простой способ настройки HTTP-клиента и возможность выполнения как синхронных, так и асинхронных запросов.

Как всегда доступен исходный код статьиover on GitHub.