异步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。