异步请求CloseableHttpAsyncClient的使用

概述

有时候,我们需要把一些没用影响业务逻辑的http请求改成异步请求,httpclient在4.0后提供的api CloseableHttpAsyncClient可以实现这种功能

提供AIO操作的api

网络调用类型

传统BIO(Blocking IO)

同步阻塞式IO服务器实现模式为一个连接一个线程

  • 客户端有连接请求时服务器端就需要启动一个线程进行处理
  • 如果这个连接不做任何事情会造成不必要的线程开销(可以通过线程池机制改善)

NIO(Not-Blocking IO)

同步非阻塞式IO服务器实现模式为一个请求一个线程

  • 即客户端发送的连接请求都会注册到多路复用器上
  • 多路复用器轮询到连接有I/O请求时才启动一个线程进行处理

AIO(NIO.2)

异步非阻塞式IO服务器实现模式为一个有效请求一个线程

  • 客户端的I/O请求都是由OS先完成了再通知服务器应用去启动线程进行处理

使用

pom引用

httpclient和httpasyncclient是两个maven包,前者提供同步api,后者提供异步api

<dependency>
	<groupId>org.apache.httpcomponents</groupId>
	<artifactId>httpclient</artifactId>
	<version>4.5.2</version>
</dependency>
<dependency>
	<groupId>org.apache.httpcomponents</groupId>
	<artifactId>httpasyncclient</artifactId>
	<version>4.1.2</version>
</dependency>

创建异步客户端

一定要设置setMaxConnTotal(并发连接最大数量)和setMaxConnPerRoute(每个域名最大并发连接数量)默认值限制的并发连接数量太小了,导致event loop线程根本跑不满,吞吐上不去

CloseableHttpAsyncClient asyncClient = HttpAsyncClientBuilder.create()
					.setMaxConnTotal(1000)
					.setMaxConnPerRoute(1000)
					.build();

启动异步线程

// 启动异步I/O线程
asyncClient.start();

构造URI

URIBuilder说明:

  1. URIBuilder既可以造出一个不可变的URI,又可以兼顾N种参数,示例如下:
    /  http://www.google.com/search?aq=f
    URI uri = new URIBuilder()
        .setScheme("http")
        .setHost("www.google.com")
        .setPath("/search")
        .setParameter("aq", "f")
        .build();
    

构造URI:

URI uri = new URIBuilder("https://www.google.com/search/aq")
.addParameter("aq", "f")
.build();

构造Get请求

URIBuilder

HttpGet request = new HttpGet(uri);

直接使用

String url = "https://www.google.com/search?aq=f";
HttpPost httpPost = new HttpPost(url);

构造Post请求

URIBuilder

HttpPost request = new HttpPost(uri);

直接使用

String url = "https://www.google.com/search?aq=f";
HttpPost httpPost = new HttpPost(url);

开启异步请求

拿着future可以wait等待完成(future原理就是一个mutex+cond)

// 提交请求到异步I/O线程
Future<HttpResponse> future = asyncClient.execute(request, null);

等待获取响应

// 等待异步请求完成
HttpResponse response = future.get();
String content = EntityUtils.toString(response.getEntity(), "utf-8");
System.out.println(content);

基于callback来处理应答

调用者的future.get()其实还是阻塞等待的,如果要做纯异步的高吞吐程序,那就直接基于callback来处理应答

// 提交请求到异步I/O线程,等待callback回调就行,不用返回的future来同步等待了
Future<HttpResponse> future = asyncClient.execute(request, new FutureCallback<HttpResponse>(){
    @Override
    public void completed(HttpResponse response) {
        try {
            String content = EntityUtils.toString(response.getEntity(), "utf-8"); 
            System.out.println(content); 
        } catch (Exception e) {
        }
    }
    @Override
    public void failed(Exception ex) {
    }
    @Override
    public void cancelled() {
    }
});

// 等一下请求完成
Thread.sleep(2000);
 
// 关闭Http客户端
asyncClient.close();

几个重要的参数

ConnectTimeout : 连接超时,连接建立时间,三次握手完成时间。

SocketTimeout : 请求超时,数据传输过程中数据包之间间隔的最大时间。

ConnectionRequestTimeout : 使用连接池来管理连接,从连接池获取连接的超时时间。

  • 如果按照远超下游处理能力的速度发起请求,httpasyncclient的request队列就会堆积,等到event loop线程终于消费到队列末尾的request时,这个request已经排队很久了,会直接被标记为超时并callback告知失败
  • 所以如果网络调用吞吐量很高的话,要么自己控制好请求发送速率,要么适当调大setConnectionRequestTimeout来允许request被处理之前排队更久

ConnTotal:连接池中最大连接数;

ConnPerRoute(1000):分配给同一个route(路由)最大的并发连接数,route为运行环境机器到目标机器的一条线路

CloseableHttpAsyncClient  client = HttpAsyncClients.custom()
					.setConnectionManager(connManager)
					.setDefaultRequestConfig(requestConfig)
					.build();
  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
可以的,下面是一个使用HttpPost异步请求的示例代码: ``` // 创建一个HttpPost对象 HttpPost httpPost = new HttpPost("http://www.example.com/api"); // 创建一个RequestConfig对象,设置请求超时时间和连接超时时间 RequestConfig requestConfig = RequestConfig.custom() .setSocketTimeout(5000) .setConnectTimeout(5000) .build(); // 设置RequestConfig对象到HttpPost对象 httpPost.setConfig(requestConfig); // 创建一个CloseableHttpAsyncClient对象 CloseableHttpAsyncClient httpAsyncClient = HttpAsyncClients.createDefault(); // 创建一个BasicNameValuePair对象列表,用于设置请求参数 List<NameValuePair> params = new ArrayList<NameValuePair>(); params.add(new BasicNameValuePair("name", "张三")); params.add(new BasicNameValuePair("age", "18")); // 创建一个UrlEncodedFormEntity对象,用于通过HttpPost请求发送参数 UrlEncodedFormEntity formEntity = new UrlEncodedFormEntity(params, "UTF-8"); // 设置UrlEncodedFormEntity对象到HttpPost对象 httpPost.setEntity(formEntity); // 发送HttpPost异步请求 httpAsyncClient.execute(httpPost, new FutureCallback<HttpResponse>() { @Override public void completed(HttpResponse response) { try { // 获取响应实体 HttpEntity responseEntity = response.getEntity(); if (responseEntity != null) { // 打印响应内容 System.out.println(EntityUtils.toString(responseEntity)); } } catch (Exception e) { e.printStackTrace(); } } @Override public void failed(Exception ex) { // 处理请求失败情况 } @Override public void cancelled() { // 处理请求取消情况 } }); // 关闭CloseableHttpAsyncClient对象 httpAsyncClient.close(); ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

?abc!

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值