异步请求官网链接:http://hc.apache.org/httpcomponents-asyncclient-4.1.x/quickstart.html
应用场景:需要把一些没用影响业务逻辑的http请求改成异步请求,提升请求效率。
HTTP 协议可能是现在 Internet 上使用得最多、最重要的协议了, JDK 的 java net包中已经提供了访问 HTTP 协议的基本功能,但是对于大部分应用程序来说。HttpClient 是Apache HttpComponents 下的子项目,用来提供高效的、最新的、功能丰富的支持 HTTP 协议的客户端编程工具包,特点:
我们知道可以用HttpClient来发送同步请求,在并发量大的情况下使用HttpClient的连接池来提高性能。此方法虽然很有效果,但是当访问量极大或网络不好的情况下也会出现某些网络请求慢导致其它请求阻塞的情况。所以我们可以将网络请求变成一个异步的请求,不影响其它的请求。
应用层的网络模型有同步与异步。同步意味当前线程是阻塞的,只有本次请求完成后才能进行下一次请求;异步意味着所有的请求可以同时塞入缓冲区,不阻塞当前的线程。
httpclient在4.x之后开始提供基于nio的异步版本httpasyncclient,httpasyncclient借助了Java并发库和nio进行封装(虽说NIO是同步非阻塞IO,但是HttpAsyncClient提供了回调的机制,与netty类似,所以可以模拟类似于AIO的效果),其调用方式非常便捷,但是其中也有许多需要注意的地方。
注:HttpClient 3 版本和 HttpClient 4 版本差别很大,代码不兼容。
(1)传统BIO(Blocking IO)
同步阻塞式IO,服务器实现模式为一个连接一个线程,即客户端有连接请求时服务器端就需要启动一个线程进行处理,如果这个连接不做任何事情会造成不必要的线程开销,当然可以通过线程池机制改善。
(2)NIO(Not-Blocking IO)
NIO:同步非阻塞式IO,服务器实现模式为一个请求一个线程,即客户端发送的连接请求都会注册到多路复用器上,多路复用器轮询到连接有I/O请求时才启动一个线程进行处理。
(3) AIO(NIO.2)
异步非阻塞式IO,服务器实现模式为一个有效请求一个线程,客户端的I/O请求都是由OS先完成了再通知服务器应用去启动线程进行处理。
CloseableHttpAsyncClient是apache在4.0后提供AIO操作的api,基本使用如下
1)pom.xml引用如下
org.apache.httpcomponents
httpclient
4.5.2
org.apache.httpcomponents
httpcore
4.4.5
org.apache.httpcomponents
httpcore-nio
4.4.5
org.apache.httpcomponents
httpasyncclient
4.1.2
异步请求示例:
package com.lei.apitest.c05_project.async;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.concurrent.FutureCallback;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.nio.client.CloseableHttpAsyncClient;
import org.apache.http.impl.nio.client.HttpAsyncClients;
import org.apache.http.impl.nio.conn.PoolingNHttpClientConnectionManager;
import org.apache.http.impl.nio.reactor.DefaultConnectingIOReactor;
import org.apache.http.impl.nio.reactor.IOReactorConfig;
import org.apache.http.nio.reactor.ConnectingIOReactor;
import org.apache.http.nio.reactor.IOReactorException;
import org.apache.http.util.EntityUtils;
import java.io.IOException;
import java.util.Vector;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicInteger;
/**
* @Author:
* @Date: 2020-09-15 15:51
* @Version: 1.0
* @Modified By:
* @Description:
*/
public class C02_AsynHttpClientV2 {
private static AtomicInteger finishedCnt = new AtomicInteger(0);
public static void main(String[] args) throws InterruptedException {
// 异步请求
long startT = System.currentTimeMillis();
Vector vector = new Vector<>();
for (int index = 0; index < 3; index++) {
MyRunThread myRunThread = new MyRunThread("threadName:" + index, 30);
vector.add(myRunThread);
myRunThread.start();
}
for (Thread thread : vector) {
thread.join();
}
long endT = System.currentTimeMillis();
long spendT = endT - startT;
System.out.println("way 2...........spendT: " + spendT);
}
static class Back implements FutureCallback{
private long start = System.currentTimeMillis();
private CountDownLatch countDownLatch;
Back(CountDownLatch countDownLatch){
this.countDownLatch = countDownLatch;
}
public void completed(HttpResponse httpResponse) {
try {
if (httpResponse.getStatusLine().getStatusCode() == 200) {
System.out.println(" finishedCnt:" + finishedCnt.incrementAndGet());
// HttpEntity entity = httpResponse.getEntity();
// String res = EntityUtils.toString(entity);
// System.out.println("cost is:"+(System.currentTimeMillis()-start)+":"+ res + " finishedCnt:" + finishedCnt.incrementAndGet());
}
} catch (Exception e) {
e.printStackTrace();
}
countDownLatch.countDown();
}
public void failed(Exception e) {
System.err.println(" cost is:"+(System.currentTimeMillis()-start)+":"+e);
countDownLatch.countDown();
}
public void cancelled() {
countDownLatch.countDown();
}
}
}
class MyRunThread extends Thread {
private String threadName;
private int runTimes;
private CountDownLatch countDownLatch;
public MyRunThread() { }
public MyRunThread(String threadName, int runTimes) {
this.threadName = threadName;
this.runTimes = runTimes;
this.countDownLatch = new CountDownLatch(runTimes);
}
@Override
public void run() {
RequestConfig requestConfig = RequestConfig.custom()
.setConnectTimeout(500000)
.setSocketTimeout(500000)
.setConnectionRequestTimeout(10000)
.build();
// 配置io线程
IOReactorConfig ioReactorConfig = IOReactorConfig.custom().
setIoThreadCount(Runtime.getRuntime().availableProcessors())
.setSoKeepAlive(true)
.build();
// 设置连接池大小
ConnectingIOReactor ioReactor = null;
try {
ioReactor = new DefaultConnectingIOReactor(ioReactorConfig);
} catch (IOReactorException e) {
e.printStackTrace();
}
PoolingNHttpClientConnectionManager connManager = new PoolingNHttpClientConnectionManager(ioReactor);
connManager.setMaxTotal(100);
connManager.setDefaultMaxPerRoute(100);
final CloseableHttpAsyncClient client = HttpAsyncClients.custom().
setConnectionManager(connManager)
.setDefaultRequestConfig(requestConfig)
.build();
// 构造请求
String url = "https://www.cnblogs.com/";
HttpPost httpPost = new HttpPost(url);
// start
client.start();
// 异步请求
long start = System.currentTimeMillis();
for (int i = 0; i < this.runTimes; i++) {
client.execute(httpPost, new C02_AsynHttpClientV2.Back(countDownLatch));
}
try {
System.err.println(this.threadName + " 全部指令发送完毕");
countDownLatch.await(); // 等待 latch1 变成0
} catch (InterruptedException e) {
e.printStackTrace();
}
long end = System.currentTimeMillis();
long spend = end - start;
System.out.println(Thread.currentThread().getName() + " spend: " + spend);
try {
client.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
几个重要的参数
同步请求示例:
package com.lei.apitest.c05_project.async;
import org.apache.http.HttpEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
/**
* @Author:
* @Date: 2020-09-15 15:51
* @Version: 1.0
* @Modified By:
* @Description:
*/
public class C02_SerialHttpClientV1 {
public static void main(String[] args) throws Exception {
// 构造请求
// String url = "http://www.baidu.com/";
String url = "https://www.cnblogs.com/";
// String url = "https://study.163.com/";
HttpPost httpPost = new HttpPost(url);
// httpPost.addHeader("Connection", "keep-alive");
httpPost.setEntity(null);
// 异步请求
long start = System.currentTimeMillis();
CloseableHttpClient httpClient = HttpClients.createDefault();
for (int i = 0; i < 90; i++) {
CloseableHttpResponse httpResponse = httpClient.execute(httpPost);
try {
if (httpResponse.getStatusLine().getStatusCode() == 200) {
System.out.println("ok: " + i);
/*HttpEntity revEntity = httpResponse.getEntity();
String res = EntityUtils.toString(revEntity);
System.out.println("cost is:"+(System.currentTimeMillis()-start)+":"+ res + " finishedCnt:" + i);*/
// System.out.println(httpResponse.getEntity().getContent().toString());
}
} finally {
httpResponse.close();
}
}
long end = System.currentTimeMillis();
long spend = end - start;
System.out.println("spend:" + spend);
httpClient.close();
}
}
小结:通过两者的请求对比,异步请求整体性能有一定幅度提升。
文章最后,给大家推荐一些受欢迎的技术博客链接:
欢迎扫描下方的二维码或 搜索 公众号“大数据高级架构师”,我们会有更多、且及时的资料推送给您,欢迎多多交流!