Contents

异步HTTP和Java中的异步HTTP-CLIENT

1. 概述

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

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

2. 设置

可以在Maven 存储库 中找到该库的最新版本。我们应该小心使用组 id 为 org.asynchttpclient的依赖项,而不是使用com.ning 的依赖项:

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

3. HTTP客户端配置

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

AsyncHttpClient client = Dsl.asyncHttpClient();

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

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 请求

有两种方法可以使用 AHC 定义 HTTP 请求:

  • bound
  • unbound

就性能而言,这两种请求类型之间没有重大区别。它们只代表我们可以用来定义请求的两个单独的 API。绑定请求与创建它的 HTTP 客户端相关联,如果未另行指定,默认情况下将使用该特定客户端的配置。

例如,在创建绑定请求时,从 HTTP 客户端配置中读取disableUrlEncoding标志,而对于未绑定请求,默认设置为 false。这很有用,因为可以通过使用作为 VM 参数传递的系统属性来更改客户端配置,而无需重新编译整个应用程序:

java -jar -Dorg.asynchttpclient.disableUrlEncodingForBoundRequests=true

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

4.1. 绑定请求

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

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

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

4.2. 未绑定请求

可以使用RequestBuilder类创建未绑定的请求:

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

或者使用Dsl helper 类,它实际上使用RequestBuilder来配置请求的 HTTP 方法和 URL:

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

5. 执行 HTTP 请求

库的名称为我们提供了有关如何执行请求的提示。AHC 支持同步和异步请求。

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

5.1.同步

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

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

尝试调试部分代码时,使用同步调用很有用,但不建议在异步执行带来更好性能和吞吐量的生产环境中使用。

5.2. 异步

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

  • AsyncHandler
  • AsyncCompletionHandler
  • ListenableFuture监听器

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

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

重要的是要提到AsyncHandler不是线程安全的,在执行并发请求时不应重用。

AsyncCompletionHandler继承了AsyncHandler接口的所有方法,并添加了onCompleted(Response)辅助方法来处理调用完成。所有其他侦听器方法都被覆盖以返回State.CONTINUE,从而使代码更具可读性:

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

ListenableFuture接口允许我们添加将在 HTTP 调用完成时运行的侦听器。

此外,它让我们通过使用另一个线程池来执行来自侦听器的代码:

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

此外,添加侦听器的选项,ListenableFuture接口允许我们将Future响应转换为CompletableFuture