HTTP asynchrone avec async-http-client en Java

Asynchrone HTTP avec async-http-client en Java

1. Vue d'ensemble

AsyncHttpClient (AHC) est une bibliothèque construite au-dessus deNetty, dans le but d'exécuter facilement les requêtes HTTP et de traiter les réponses de manière asynchrone.

Dans cet article, nous présenterons comment configurer et utiliser le client HTTP, comment exécuter une requête et traiter la réponse à l'aide d'AHC.

2. Installer

La dernière version de la bibliothèque se trouve dans lesMaven repository. Nous devons faire attention à utiliser la dépendance avec l'id de groupeorg.asynchttpclient et non celle aveccom.ning:


    org.asynchttpclient
    async-http-client
    2.2.0

3. Configuration du client HTTP

La méthode la plus simple pour obtenir le client HTTP consiste à utiliser la classeDsl. La méthode statiqueasyncHttpClient() renvoie un objetAsyncHttpClient:

AsyncHttpClient client = Dsl.asyncHttpClient();

Si nous avons besoin d'une configuration personnalisée du client HTTP, nous pouvons construire l'objetAsyncHttpClient en utilisant le générateurDefaultAsyncHttpClientConfig.Builder:

DefaultAsyncHttpClientConfig.Builder clientBuilder = Dsl.config()

Cela offre la possibilité de configurer des délais d'attente, un serveur proxy, des certificats HTTP et bien d'autres:

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

Une fois que nous avons configuré et obtenuan instance of the HTTP client we can reuse it across out application. Nous n'avons pas besoin de créer une instance pour chaque requête, car en interne, cela crée de nouveaux threads et pools de connexions, ce qui entraînera des problèmes de performances.

En outre, il est important de noter queonce we’ve finished using the client we should call to close() method to prevent any memory leaks ou ressources suspendues.

4. Création d'une requête HTTP

Il existe deux méthodes pour définir une requête HTTP à l'aide d'AHC:

  • lié

  • non lié

Il n'y a pas de différence majeure entre les deux types de demandes en termes de performances. Ils ne représentent que deux API distinctes que nous pouvons utiliser pour définir une demande. 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.

Par exemple, lors de la création d'une requête liée, l'indicateurdisableUrlEncoding est lu à partir de la configuration du client HTTP, tandis que pour une requête non liée, il est défini par défaut sur false. Cela est utile car la configuration du client peut être modifiée sans recompiler l'application complète à l'aide des propriétés système transmises en tant qu'arguments de la machine virtuelle:

java -jar -Dorg.asynchttpclient.disableUrlEncodingForBoundRequests=true

Une liste complète des propriétés se trouve dans le fichierahc-default.properties.

4.1. Demande liée

Pour créer une requête liée, nous utilisons les méthodes d'assistance de la classeAsyncHttpClient qui commencent par le préfixe“prepare”. De plus, nous pouvons utiliser la méthodeprepareRequest() qui reçoit un objetRequest déjà créé.

Par exemple, la méthodeprepareGet() créera une requête HTTP GET:

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

4.2. Demande non liée

Une demande indépendante peut être créée à l'aide de la classeRequestBuilder:

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

ou en utilisant la classe d'assistanceDsl, qui utilise en fait lesRequestBuilder pour configurer la méthode HTTP et l'URL de la requête:

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

5. Exécuter des requêtes HTTP

Le nom de la bibliothèque nous indique comment les requêtes peuvent être exécutées. AHC prend en charge les demandes synchrones et asynchrones.

L'exécution de la demande dépend de son type. Lors de l'utilisation d'une classebound request we use the execute() method from the BoundRequestBuilder et lorsque nous avons ununbound request we’ll execute it using one of the implementations of the executeRequest() method from the AsyncHttpClient interface.

5.1. De manière synchrone

La bibliothèque a été conçue pour être asynchrone, mais lorsque cela est nécessaire, nous pouvons simuler des appels synchrones en bloquant sur l'objetFuture. Les méthodesexecute() etexecuteRequest() renvoient un objetListenableFuture<Response>. Cette classe étend l'interface de JavaFuture, héritant ainsi de la méthodeget(), qui peut être utilisée pour bloquer le thread actuel jusqu'à ce que la requête HTTP soit terminée et renvoie une réponse:

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

L'utilisation d'appels synchrones est utile lorsque vous essayez de déboguer des parties de notre code, mais il n'est pas recommandé de l'utiliser dans un environnement de production où les exécutions asynchrones conduisent à de meilleures performances et un meilleur débit.

5.2. Asynchrone

Lorsque nous parlons d'exécutions asynchrones, nous parlons également d'écouteurs pour le traitement des résultats. La bibliothèque AHC fournit 3 types d'écouteurs pouvant être utilisés pour les appels HTTP asynchrones:

  • AsyncHandler

  • AsyncCompletionHandler

  • ListenableFuture auditeurs

L'auditeurAsyncHandler offre la possibilité de contrôler et de traiter l'appel HTTP avant qu'il ne soit terminé. Son utilisation peut gérer une série d'événements liés à l'appel 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;
    }
});


L'énumérationState nous permet de contrôler le traitement de la requête HTTP. En renvoyantState.ABORT we can stop the processing à un moment précis et en utilisantState.CONTINUE nous laissons le traitement se terminer.

Il est important de mentionner que lesAsyncHandler isn’t thread-safe and shouldn’t be reused when executing concurrent requests.

AsyncCompletionHandler hérite de toutes les méthodes de l'interfaceAsyncHandler et ajoute la méthode d'assistanceonCompleted(Response) pour gérer l'achèvement de l'appel. Toutes les autres méthodes d'écoute sont remplacées pour renvoyerState.CONTINUE, rendant ainsi le code plus lisible:

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


L'interface deListenableFuture nous permet d'ajouter des écouteurs qui s'exécuteront lorsque l'appel HTTP sera terminé.

En outre, nous allons exécuter le code à partir des écouteurs - en utilisant un autre pool de threads:

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

De plus, la possibilité d'ajouter des écouteurs, l'interfaceListenableFuture nous permet de transformer la réponseFuture enCompletableFuture.

7. Conclusion

AHC est une bibliothèque très puissante, avec beaucoup de fonctionnalités intéressantes. Il offre un moyen très simple de configurer un client HTTP et la possibilité d’exécuter des requêtes synchrones et asynchrones.

Comme toujours, le code source de l'article est disponibleover on GitHub.