异步请求CloseableHttpAsyncClient的使用

浪子不回头ぞ 提交于 2020-10-07 04:20:51

1、前言

项目有个需求,需要把一些没用影响业务逻辑的http请求改成异步请求,httpclient在4.0后提供新的api CloseableHttpAsyncClient可以使用,记录下使用过程。

2、网络调用类型

(1)传统BIO(Blocking IO)

同步阻塞式IO,服务器实现模式为一个连接一个线程,即客户端有连接请求时服务器端就需要启动一个线程进行处理,如果这个连接不做任何事情会造成不必要的线程开销,当然可以通过线程池机制改善。

(2)NIO(Not-Blocking IO)

NIO:同步非阻塞式IO,服务器实现模式为一个请求一个线程,即客户端发送的连接请求都会注册到多路复用器上,多路复用器轮询到连接有I/O请求时才启动一个线程进行处理。

(3)AIO(NIO.2)

异步非阻塞式IO,服务器实现模式为一个有效请求一个线程,客户端的I/O请求都是由OS先完成了再通知服务器应用去启动线程进行处理。

3、CloseableHttpAsyncClient

CloseableHttpAsyncClient是apache在4.0后提供AIO操作的api,基本使用如下

1)pom.xml引用如下

<dependency>
	<groupId>org.apache.httpcomponents</groupId>
	<artifactId>httpclient</artifactId>
	<version>4.5.2</version>
</dependency>
<dependency>
	<groupId>org.apache.httpcomponents</groupId>
	<artifactId>httpcore</artifactId>
	<version>4.4.5</version>
</dependency>
<dependency>
	<groupId>org.apache.httpcomponents</groupId>
	<artifactId>httpcore-nio</artifactId>
	<version>4.4.5</version>
</dependency>
<dependency>
	<groupId>org.apache.httpcomponents</groupId>
	<artifactId>httpasyncclient</artifactId>
	<version>4.1.2</version>
</dependency>

 异步请求示例:

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<Thread> 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<HttpResponse>{

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

几个重要的参数

  • ConnectTimeout : 连接超时,连接建立时间,三次握手完成时间。
  • SocketTimeout : 请求超时,数据传输过程中数据包之间间隔的最大时间。
  • ConnectionRequestTimeout : 使用连接池来管理连接,从连接池获取连接的超时时间。
  • ConnTotal:连接池中最大连接数;
  • ConnPerRoute(1000):分配给同一个route(路由)最大的并发连接数,route为运行环境机器到目标机器的一条线路

同步请求示例:

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

文章最后,给大家推荐一些受欢迎的技术博客链接

  1. JAVA相关的深度技术博客链接
  2. Flink 相关技术博客链接
  3. Spark 核心技术链接
  4. 设计模式 —— 深度技术博客链接
  5. 机器学习 —— 深度技术博客链接
  6. Hadoop相关技术博客链接
  7. 超全干货--Flink思维导图,花了3周左右编写、校对
  8. 深入JAVA 的JVM核心原理解决线上各种故障【附案例】
  9. 请谈谈你对volatile的理解?--最近小李子与面试官的一场“硬核较量”
  10. 聊聊RPC通信,经常被问到的一道面试题。源码+笔记,包懂
  11. 深入聊聊Java 垃圾回收机制【附原理图及调优方法】

欢迎扫描下方的二维码或 搜索 公众号“大数据高级架构师”,我们会有更多、且及时的资料推送给您,欢迎多多交流!

                                           

       

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!