HTTP assíncrono com async-http-client em Java

HTTP assíncrono com async-http-client em Java

1. Visão geral

AsyncHttpClient (AHC) é uma biblioteca construída em cima deNetty, com o objetivo de executar facilmente solicitações HTTP e processar respostas de forma assíncrona.

Neste artigo, apresentaremos como configurar e usar o cliente HTTP, como executar uma solicitação e processar a resposta usando AHC.

2. Configuração

A versão mais recente da biblioteca pode ser encontrada emMaven repository. Devemos ter o cuidado de usar a dependência com o id de grupoorg.asynchttpclient e não aquela comcom.ning:


    org.asynchttpclient
    async-http-client
    2.2.0

3. Configuração do Cliente HTTP

O método mais direto de obter o cliente HTTP é usando a classeDsl. O métodoasyncHttpClient() estático retorna um objetoAsyncHttpClient:

AsyncHttpClient client = Dsl.asyncHttpClient();

Se precisarmos de uma configuração personalizada do cliente HTTP, podemos construir o objetoAsyncHttpClient usando o construtorDefaultAsyncHttpClientConfig.Builder:

DefaultAsyncHttpClientConfig.Builder clientBuilder = Dsl.config()

Isso oferece a possibilidade de configurar tempos limite, um servidor proxy, certificados HTTP e muito mais:

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

Depois de configurar e obteran instance of the HTTP client we can reuse it across out application. Não precisamos criar uma instância para cada solicitação, porque internamente cria novos threads e pools de conexão, o que levará a problemas de desempenho.

Além disso, é importante observar queonce we’ve finished using the client we should call to close() method to prevent any memory leaks ou recursos suspensos.

4. Criando uma solicitação HTTP

Existem dois métodos nos quais podemos definir uma solicitação HTTP usando o AHC:

  • limite

  • sem limites

Não há grande diferença entre os dois tipos de solicitação em termos de desempenho. Eles representam apenas duas APIs separadas que podemos usar para definir uma solicitação. 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.

Por exemplo, ao criar uma solicitação vinculada, o sinalizadordisableUrlEncoding é lido na configuração do cliente HTTP, enquanto para uma solicitação não vinculada, por padrão, é definido como falso. Isso é útil porque a configuração do cliente pode ser alterada sem recompilar o aplicativo inteiro usando as propriedades do sistema passadas como argumentos da VM:

java -jar -Dorg.asynchttpclient.disableUrlEncodingForBoundRequests=true

Uma lista completa de propriedades pode ser encontrada no arquivoahc-default.properties.

4.1. Solicitação vinculada

Para criar uma solicitação de limite, usamos os métodos auxiliares da classeAsyncHttpClient que começam com o prefixo“prepare”. Além disso, podemos usar o métodoprepareRequest() que recebe um objetoRequest já criado.

Por exemplo, o métodoprepareGet() criará uma solicitação HTTP GET:

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

4.2. Solicitação não vinculada

Uma solicitação não vinculada pode ser criada usando a classeRequestBuilder:

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

ou usando a classe auxiliarDsl, que na verdade usaRequestBuilder para configurar o método HTTP e o URL da solicitação:

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

5. Executando solicitações HTTP

O nome da biblioteca nos dá uma dica sobre como as solicitações podem ser executadas. O AHC tem suporte para solicitações síncronas e assíncronas.

A execução da solicitação depende do seu tipo. Ao usar uma classebound request we use the execute() method from the BoundRequestBuilder e quando temos umunbound request we’ll execute it using one of the implementations of the executeRequest() method from the AsyncHttpClient interface.

5.1. Synchronously

A biblioteca foi projetada para ser assíncrona, mas quando necessário, podemos simular chamadas síncronas bloqueando o objetoFuture. Os métodosexecute()eexecuteRequest() retornam um objetoListenableFuture<Response>. Esta classe estende a interface JavaFuture, herdando assim o métodoget(), que pode ser usado para bloquear o encadeamento atual até que a solicitação HTTP seja concluída e retorne uma resposta:

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

Usar chamadas síncronas é útil ao tentar depurar partes do nosso código, mas não é recomendado para ser usado em um ambiente de produção onde as execuções assíncronas levam a melhor desempenho e rendimento.

5.2. Assíncrona

Quando falamos sobre execuções assíncronas, também falamos sobre ouvintes para processar os resultados. A biblioteca AHC fornece 3 tipos de ouvintes que podem ser usados ​​para chamadas HTTP assíncronas:

  • AsyncHandler

  • AsyncCompletionHandler

  • ListenableFuture ouvintes

O ouvinteAsyncHandler oferece a possibilidade de controlar e processar a chamada HTTP antes que ela seja concluída. Usá-lo pode lidar com uma série de eventos relacionados à chamada 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;
    }
});


O enumState nos permite controlar o processamento da solicitação HTTP. RetornandoState.ABORT we can stop the processing em um momento específico e usandoState.CONTINUE, deixamos o processamento terminar.

É importante mencionar que oAsyncHandler isn’t thread-safe and shouldn’t be reused when executing concurrent requests.

AsyncCompletionHandler herda todos os métodos da interfaceAsyncHandler e adiciona o método auxiliaronCompleted(Response) para lidar com a conclusão da chamada. Todos os outros métodos de ouvinte são substituídos para retornarState.CONTINUE, tornando o código mais legível:

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


A interfaceListenableFuture nos permite adicionar ouvintes que serão executados quando a chamada HTTP for concluída.

Além disso, vamos executar o código dos ouvintes - usando outro pool de threads:

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

Além da opção de adicionar ouvintes, a interfaceListenableFuture permite transformar a resposta deFuture emCompletableFuture.

7. Conclusão

O AHC é uma biblioteca muito poderosa, com muitos recursos interessantes. Ele oferece uma maneira muito simples de configurar um cliente HTTP e a capacidade de executar solicitações síncronas e assíncronas.

Como sempre, o código-fonte do artigo está disponívelover on GitHub.