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

1. Общ преглед

AsyncHttpClient (AHC) е библиотека, изградена върху Netty, с цел лесно изпълнение на HTTP заявки и обработка на отговорите асинхронно.

В тази статия ще ви представим как да конфигурирате и използвате HTTP клиента, как да изпълните заявка и да обработите отговора с помощта на AHC.

2. Настройка

Най-новата версия на библиотеката може да бъде намерена в хранилището на Maven. Трябва да внимаваме да използваме зависимостта с идентификатора на групата 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);

След като конфигурираме и получим екземпляр на HTTP клиента, можем да го използваме повторно в цялото приложение . Не е необходимо да създаваме екземпляр за всяка заявка, защото вътрешно тя създава нови нишки и пулове от връзки, което ще доведе до проблеми с производителността.

Също така е важно да се отбележи, че след като приключим с използването на клиента, трябва да се обадим на метод close () , за да предотвратим изтичане на памет или окачване на ресурси.

4. Създаване на HTTP заявка

Има два метода, при които можем да дефинираме HTTP заявка, използвайки AHC:

  • обвързан
  • необвързан

Няма съществена разлика между двата типа заявки по отношение на изпълнението. Те представляват само два отделни API, които можем да използваме за дефиниране на заявка. Обвързаната заявка е обвързана с HTTP клиента, от който е създадена и по подразбиране ще използва конфигурацията на този конкретен клиент, ако не е посочено друго.

Например, когато създавате обвързана заявка, флагът на disableUrlEncoding се чете от конфигурацията на HTTP клиента, докато за незавързана заявка това по подразбиране е зададено на false. Това е полезно, защото конфигурацията на клиента може да бъде променена, без да се прекомпилира цялото приложение, като се използват системни свойства, предадени като VM аргументи:

java -jar -Dorg.asynchttpclient.disableUrlEncodingForBoundRequests=true

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

4.1. Обвързано искане

За да създадем обвързана заявка, използваме помощните методи от класа AsyncHttpClient, които започват с префикса „подготвяне“ . Също така можем да използваме метода prepaRequest () , който получава вече създаден обект Request .

Например методът pripremGet () ще създаде HTTP GET заявка:

BoundRequestBuilder getRequest = client.prepareGet("//www.baeldung.com");

4.2. Неконсолидирана заявка

Може да се създаде необвързана заявка с помощта на класа RequestBuilder :

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

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

Request getRequest = Dsl.get("//www.baeldung.com").build()

5. Изпълнение на HTTP заявки

Името на библиотеката ни дава намек за това как заявките могат да бъдат изпълнени. AHC има поддръжка както за синхронни, така и за асинхронни заявки.

Изпълнението на заявката зависи от нейния тип. Когато използваме обвързана заявка, използваме метода execute () от класа BoundRequestBuilder и когато имаме несвързана заявка, ще я изпълним, като използваме една от реализациите на метода executeRequest () от интерфейса AsyncHttpClient .

5.1. Синхронно

Библиотеката е проектирана да бъде асинхронна, но когато е необходимо, можем да симулираме синхронни повиквания, като блокираме обекта Future . И двата метода execute () и executeRequest () връщат обект ListenableFuture . Този клас разширява интерфейса на Java Future , като по този начин наследява метода get () , който може да се използва за блокиране на текущата нишка до завършване на HTTP заявката и връщане на отговор:

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

Използването на синхронни обаждания е полезно при опит за отстраняване на грешки на части от нашия код, но не се препоръчва да се използва в производствена среда, където асинхронните изпълнения водят до по-добра производителност и производителност.

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

When we talk about asynchronous executions, we also talk about listeners for processing the results. The AHC library provides 3 types of listeners that can be used for asynchronous HTTP calls:

  • AsyncHandler
  • AsyncCompletionHandler
  • ListenableFuture listeners

The AsyncHandler listener offers the possibility to control and process the HTTP call before it has completed. Using it can handle a series of events related to the HTTP call:

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; } });

The State enum lets us control the processing of the HTTP request. By returning State.ABORT we can stop the processing at a specific moment and by using State.CONTINUE we let the processing finish.

It's important to mention that the AsyncHandler isn't thread-safe and shouldn't be reused when executing concurrent requests.

AsyncCompletionHandler inherits all the methods from the AsyncHandler interface and adds the onCompleted(Response) helper method for handling the call completion. All the other listener methods are overridden to return State.CONTINUE, thus making the code more readable:

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

The ListenableFuture interface lets us add listeners that will run when the HTTP call is completed.

Also, it let's execute the code from the listeners – by using another thread pool:

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

Освен това опцията за добавяне на слушатели, интерфейсът ListenableFuture ни позволява да трансформираме бъдещия отговор в CompletableFuture .

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

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

Както винаги, изходният код на статията е достъпен в GitHub.