Asynchronous HTTP with async-http-client in Java – 在Java中使用async-http-client的异步HTTP

最后修改: 2018年 2月 11日

中文/混合/英文(键盘快捷键:t)

1. Overview

1.概述

AsyncHttpClient (AHC) is a library build on top of Netty, with the purpose of easily executing HTTP requests and processing responses asynchronously.

AsyncHttpClient(AHC)是一个建立在Netty之上的库,其目的是轻松执行HTTP请求并异步处理响应。

In this article, we’ll present how to configure and use the HTTP client, how to execute a request and process the response using AHC.

在这篇文章中,我们将介绍如何配置和使用HTTP客户端,如何使用AHC执行一个请求和处理响应。

2. Setup

2.设置

The latest version of the library can be found in the Maven repository. We should be careful to use the dependency with the group id org.asynchttpclient and not the one with com.ning:

该库的最新版本可以在Maven资源库中找到。我们应该注意使用组名为org.asynchttpclient的依赖关系,而不是com.ning:的那个。

<dependency>
    <groupId>org.asynchttpclient</groupId>
    <artifactId>async-http-client</artifactId>
    <version>2.2.0</version>
</dependency>

3. HTTP Client Configuration

3.HTTP客户端配置

The most straightforward method of obtaining the HTTP client is by using the Dsl class. The static asyncHttpClient() method returns an AsyncHttpClient object:

获得HTTP客户端的最直接的方法是使用Dsl类。静态asyncHttpClient()方法返回一个AsyncHttpClient对象。

AsyncHttpClient client = Dsl.asyncHttpClient();

If we need a custom configuration of the HTTP client, we can build the AsyncHttpClient object using the builder DefaultAsyncHttpClientConfig.Builder:

如果我们需要HTTP客户端的自定义配置,我们可以使用构建器DefaultAsyncHttpClientConfig.Builder建立AsyncHttpClient对象。

DefaultAsyncHttpClientConfig.Builder clientBuilder = Dsl.config()

This offers the possibility to configure timeouts, a proxy server, HTTP certificates and many more:

这提供了配置超时、代理服务器、HTTP证书和更多的可能性。

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

Once we’ve configured and obtained an instance of the HTTP client we can reuse it across out application. We don’t need to create an instance for each request because internally it creates new threads and connection pools, which will lead to performance issues.

一旦我们配置并获得了HTTP客户端的实例,我们就可以在整个应用中重复使用它。我们不需要为每个请求创建一个实例,因为在内部会创建新的线程和连接池,这将导致性能问题。

Also, it’s important to note that once we’ve finished using the client we should call to close() method to prevent any memory leaks or hanging resources.

另外,需要注意的是,一旦我们使用完客户端,我们应该调用close()方法,以防止任何内存泄漏或挂起资源。

4. Creating an HTTP Request

4.创建一个HTTP请求

There are two methods in which we can define an HTTP request using AHC:

有两种方法,我们可以用AHC定义一个HTTP请求。

  • bound
  • unbound

There is no major difference between the two request types in terms of performance. They only represent two separate APIs we can use to define a request. 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.

在性能方面,这两种请求类型没有大的区别。它们只是代表了我们可以用来定义一个请求的两个独立的API。一个绑定的请求与它所创建的HTTP客户端联系在一起,如果没有另外指定,默认情况下将使用该特定客户端的配置。

For example, when creating a bound request the disableUrlEncoding flag is read from the HTTP client configuration, while for an unbound request this is, by default set to false. This is useful because the client configuration can be changed without recompiling the whole application by using system properties passed as VM arguments:

例如,在创建一个绑定的请求时,disableUrlEncoding标志从HTTP客户端配置中读取,而对于非绑定的请求,默认情况下,该标志被设置为假。这很有用,因为通过使用作为VM参数传递的系统属性,可以在不重新编译整个应用程序的情况下改变客户端配置。

java -jar -Dorg.asynchttpclient.disableUrlEncodingForBoundRequests=true

A complete list of properties can be found the ahc-default.properties file.

完整的属性列表可以在ahc-default.properties文件中找到。

4.1. Bound Request

4.1.绑定请求

To create a bound request we use the helper methods from the class AsyncHttpClient that start with the prefix “prepare”. Also, we can use the prepareRequest() method which receives an already created Request object.

为了创建一个绑定的请求,我们使用AsyncHttpClient类中以“prepare”为前缀的辅助方法。此外,我们还可以使用prepareRequest()方法,该方法接收一个已经创建的Request对象。

For example, the prepareGet() method will create an HTTP GET request:

例如,prepareGet()方法将创建一个HTTP GET请求。

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

4.2. Unbound Request

4.2.无约束请求

An unbound request can be created using the RequestBuilder class:

可以使用RequestBuilder类创建一个非绑定请求。

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

or by using the Dsl helper class, which actually uses the RequestBuilder for configuring the HTTP method and URL of the request:

或者通过使用Dsl辅助类,它实际上使用RequestBuilder来配置HTTP方法和请求的URL。

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

5. Executing HTTP Requests

5.执行HTTP请求

The name of the library gives us a hint about how the requests can be executed. AHC has support for both synchronous and asynchronous requests.

库的名字给了我们一个关于如何执行请求的提示。AHC对同步和异步请求都有支持。

Executing the request depends on its type. When using a bound request we use the execute() method from the BoundRequestBuilder class and when we have an unbound request we’ll execute it using one of the implementations of the executeRequest() method from the AsyncHttpClient interface.

执行请求取决于其类型。当使用绑定请求时,我们使用BoundRequestBuilder类中的execute()方法,当我们有一个非绑定请求时,我们将使用AsyncHttpClient接口中的executeRequest()方法的实现之一来执行它

5.1. Synchronously

5.1.同步进行

The library was designed to be asynchronous, but when needed we can simulate synchronous calls by blocking on the Future object. Both execute() and executeRequest() methods return a ListenableFuture<Response> object. This class extends the Java Future interface, thus inheriting the get() method, which can be used to block the current thread until the HTTP request is completed and returns a response:

该库被设计成异步的,但在需要时,我们可以通过对Future对象进行阻塞来模拟同步调用。execute()executeRequest()方法都返回一个ListenableFuture<Response>对象。这个类扩展了Java的Future接口,因此继承了get()方法,该方法可用于阻塞当前线程,直到HTTP请求完成并返回响应。

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

Using synchronous calls is useful when trying to debug parts of our code, but it’s not recommended to be used in a production environment where asynchronous executions lead to better performance and throughput.

在试图调试我们代码的一部分时,使用同步调用是有用的,但不建议在生产环境中使用,因为异步执行会带来更好的性能和吞吐量。

5.2. Asynchronously

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:

当我们谈论异步执行时,我们也谈论处理结果的监听器。AHC库提供了3种类型的监听器,可用于异步的HTTP调用。

  • 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:

AsyncHandler监听器提供了在HTTP调用完成之前控制和处理的可能性。使用它可以处理与HTTP调用相关的一系列事件。

request.execute(new AsyncHandler<Object>() {
    @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.

State枚举让我们控制HTTP请求的处理。通过返回State.ABORT,我们可以在一个特定的时刻停止处理,通过使用State.CONTINUE,我们让处理结束。

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

值得一提的是,AsyncHandler并不是线程安全的,在执行并发请求时不应该被重复使用。

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:

AsyncCompletionHandler继承了AsyncHandler接口的所有方法,并添加了onCompleted(Response) 帮助方法来处理调用完成。所有其他的监听器方法都被重载,以返回State.CONTINUE,从而使代码更容易阅读。

request.execute(new AsyncCompletionHandler<Object>() {
    @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.

ListenableFuture接口让我们可以添加监听器,在HTTP调用完成后运行。

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

另外,它让我们通过使用另一个线程池来执行听众的代码。

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

Besides, the option to add listeners, the ListenableFuture interface lets us transform the Future response to a CompletableFuture.

除了添加监听器的选项,ListenableFuture接口让我们可以将Future响应转化为CompletableFuture

7. Conclusion

7.结语

AHC is a very powerful library, with a lot of interesting features. It offers a very simple way to configure an HTTP client and the capability of executing both synchronous and asynchronous requests.

AHC是一个非常强大的库,有很多有趣的功能。它提供了一个非常简单的方法来配置一个HTTP客户端,并具有执行同步和异步请求的能力。

As always, the source code for the article is available over on GitHub.

一如既往,该文章的源代码可在GitHub上获取。