一、前言

HttpClient提供了两种I/O模型:经典的java阻塞I/O模型和基于Java NIO的异步非阻塞事件驱动I/O模型。

Java中的阻塞I/O是一种高效、便捷的I/O模型,非常适合并发连接数量相对适中的高性能应用程序。只要并发连接的数量在1000个以下并且连接大多忙于传输数据,阻塞I/O模型就可以提供最佳的数据吞吐量性能。然而,对于连接大部分时间保持空闲的应用程序,上下文切换的开销可能会变得很大,这时非阻塞I/O模型可能会提供更好的替代方案。

异步I/O模型可能更适合于比较看重资源高效利用、系统可伸缩性、以及可以同时支持更多HTTP连接的场景。

二、HttpClient中的Future

在HttpClient官网Tutorial的高级话题中,我们可以发现其提供了用于异步执行的FutureRequestExecutionService服务类。

使用FutureRequestExecutionService,允许我们发起http调用后,调用函数马上返回(调用线程不会阻塞等到相应结果返回)一个Future对象,然后调用线程可以在需要响应结果的地方调用Future对象的get方法来阻塞等待结果。

使用FutureRequestExecutionService的优点是,我们可以使用多个线程并发调度请求、设置任务超时,或者在不再需要响应时取消它们。

FutureRequestExecutionService其实是用一个HttpRequestFutureTask包装请求,该HttpRequestFutureTask扩展了JDK中的FutureTask。这个类允许我们取消任务、跟踪各种执行指标,如请求持续时间等。

下面我们看一个例子:

// 1.创建线程池

private static ExecutorService executorService = Executors.newFixedThreadPool(5);

// 2.创建http回调函数

private static final class OkidokiHandler implements ResponseHandler

public String handleResponse(final HttpResponse response) throws ClientProtocolException, IOException {

// 2.1处理响应结果

return EntityUtils.toString(response.getEntity());

}

}

public static void main(String[] args) throws IOException, InterruptedException, ExecutionException {

// 3.创建httpclient对象

CloseableHttpClient httpclient = HttpClients.createDefault();

// 4.创建FutureRequestExecutionService实例

FutureRequestExecutionService futureRequestExecutionService = new FutureRequestExecutionService(httpclient,

executorService);

// 5.发起调用

try {

// 5.1请求参数

HttpGet httpget1 = new HttpGet("http://127.0.0.1:8080/test1");

HttpGet httpget2 = new HttpGet("http://127.0.0.1:8080/test2");

// 5.2发起请求,不阻塞,马上返回

HttpRequestFutureTask

HttpClientContext.create(), new OkidokiHandler());

HttpRequestFutureTask

HttpClientContext.create(), new OkidokiHandler());

// 5.3 do somthing

// 5.4阻塞获取结果

String str1 = task1.get();

String str2 = task2.get();

System.out.println("response:" + str1 + " " + str2);

} finally {

httpclient.close();

}

}

如上代码1创建了一个线程池用来作为http异步执行的后台线程,代码2创建了一个http响应结果处理器,用来异步处理http的响应结果。

代码3创建了一个HttpClient对象,代码4创建一个FutureRequestExecutionService,参数1为创建的httpclient对象,参数2为创建的线程池。

代码5则创建2个Get请求参数,然后执行代码5.2发起两个http请求,该调用会马上返回自己对于的HttpRequestFutureTask对象,调用线程也会马上返回,然后调用线程就可以在5.3做其他的事情,最后在需要获取http响应结果的地方,比如代码5.4调用两个future的get()方法来获取结果。

如上基于Future方式,我们可以并发的发起两个http请求,而之前阻塞方式,则是顺序执行的。

但是基于上面Future方式,我们调用线程还是会在代码5.4阻塞等待响应结果,这并不是我们想要的,我们想要的是事件通知,http确实也提供了注册CallBack的方式:

首先我们需要实现FutureCallback接口,用来接收通知:

private static final class MyCallback implements FutureCallback

public void failed(final Exception ex) {

System.out.println(ex.getLocalizedMessage());

}

public void completed(final String result) {

System.out.println(result);

}

public void cancelled() {

System.out.println("cancelled");

}

}

然后我们只需要修改代码5.2,使用三个参数的execute方法发起调用:

// 5.发起调用

try {

// 5.1请求参数

HttpGet httpget1 = new HttpGet("http://127.0.0.1:8080/test1");

HttpGet httpget2 = new HttpGet("http://127.0.0.1:8080/test2");

// 5.2发起请求,不阻塞,马上返回

HttpRequestFutureTask

HttpClientContext.create(), new OkidokiHandler(), new MyCallback());

HttpRequestFutureTask

HttpClientContext.create(), new OkidokiHandler(), new MyCallback());

//main线程休眠10s,避免请求结束前,关闭了链接

Thread.sleep(10000);

...

如上代码,使用CallBack后,调用线程就得到了彻底解放,就不必再阻塞获取结果了,当http返回结果后,会自动调用我们注册的CallBack。

三、HttpAsyncClient-真正的异步

上面HttpClient提供的CallBack的方式,虽然解放了调用线程,但是并不是真正意义上的异步调用,因为其异步调用的支持是基于我们创建的executorService线程。即:虽然发起http调用后,调用线程马上返回了,但是其内部还是使用executorService中的一个线程阻塞等待响应结果。

HttpAsyncClient则使用Java NIO的异步非阻塞事件驱动I/O模型,实现了真正意义的异步调用,使用HttpAsyncClient我们需要引入其专门的包:

然后改造后代码如下:

// 1.创建CallBack

private static final class MyCallback implements FutureCallback

public void failed(final Exception ex) {

System.out.println(ex.getLocalizedMessage());

}

public void completed(final HttpResponse response) {

try {

System.out.println(EntityUtils.toString(response.getEntity()));

} catch (ParseException e) {

e.printStackTrace();

} catch (IOException e) {

e.printStackTrace();

}

}

public void cancelled() {

System.out.println("cancelled");

}

}

public static void main(String[] args) throws IOException, InterruptedException, ExecutionException {

// 2.创建异步httpclient对象

CloseableHttpAsyncClient httpclient = HttpAsyncClients.custom().build();

// 3.发起调用

try {

// 3.0启动

httpclient.start();

// 3.1请求参数

HttpGet httpget1 = new HttpGet("http://127.0.0.1:8080/test1");

HttpGet httpget2 = new HttpGet("http://127.0.0.1:8080/test2");

// 3.2发起请求,不阻塞,马上返回

httpclient.execute(httpget1, new MyCallback());

httpclient.execute(httpget2, new MyCallback());

// 3.3休眠10s,避免请求执行完成前,关闭了链接

Thread.sleep(10000);

} finally {

httpclient.close();

}

}

如上代码1,创建异步回调实现,用于处理Http响应结果。代码2创建了异步HttpClient,代码3.0启动client,代码3.2发起请求。

基于Java NIO的异步,当发起请求后,调用方不会使用任何线程同步等待http服务端的响应结果(少量的NIO线程不算哦,因为其个数固定,并且不随并发请求数量变化),而是会使用少量内存来记录请求信息,以便服务端响应结果回来后,可以找到对应的回调函数进行执行。

四、总结

本文概要讲解了Http的异步调用,关于更多Java中异步调用与异步执行的知识,可以参考《Java异步编程实战》

更多技术分享,请扫描关注微信公众号:

file0人点赞博文

java http异步调用_HttpClient的异步调用,你造吗?相关推荐

  1. java 异步定时任务_spring 定时任务 异步调用

    本篇仅限于 Spring 定时任务 & 异步调用的基本使用,不涉及深入原理探究,先学会怎么用,再探究原理. 环境:SpringBoot 2.0 定时任务指的是应用程序在指定的时间执行预先定义好 ...

  2. java同步转化成异步_Java 如何把异步调用模拟成同步调用

    在某些时候,须要把异步调用模拟成同步调用的形态.例如,基于基于异步通讯的客户端须要同步调用. :-)异步 要实现这个转换,能够有多种实现方法:this 1.很经常使用的方法,应用循环机制:spa bo ...

  3. ADDCOMPONENT之后立即(同步)调用AWAKE,但START却是所有AWAKE完成后才调用 的(异步)...

    ADDCOMPONENT之后立即(同步)调用AWAKE,但START却是所有AWAKE完成后才调用 的(异步) addcomponent 等价于 instance了一个组件,完成后立即从该点调用awa ...

  4. ExtJs异步ajax调用和同步ajax调用公用方法

    Js代码 //异步ajax调用 /** * 异步调用ajax,成功后返回值,作为回调函数的参数 调用失败会提示 * * @param {} *            urlStr * @param { ...

  5. loading怎么关闭 vant_vant-ui组件调用Dialog弹窗异步关闭操作

    需求描述: 需求描述:官方文档又是组件调用方式,又是函数调用方式. 我就需要一个很简单的:点击操作弹窗显示后,我填写一个表单,表单校验通过后,再调用API接口,返回成功后,关闭弹窗. 一个很简单的东西 ...

  6. google的api key调用次数是多少_Sprint Boot如何基于Redis发布订阅实现异步消息系统的同步调用?...

    前言 在很多互联网应用系统中,请求处理异步化是提升系统性能一种常用的手段,而基于消息系统的异步处理由于具备高可靠性.高吞吐量的特点,因而在并发请求量比较高的互联网系统中被广泛应用.与此同时,这种方案也 ...

  7. 同步异步 阻塞 非阻塞 异步调用 线程队列 协程

    阻塞 非阻塞 阻塞:程序遇到了IO操作 导致代码无法继续执行 交出了COU执行权 非阻塞:没有IO操作 或者即使遇到IO操作 也不阻塞代码执行 阻塞 就绪 运行指的是应用程序所处的状态写程序时 尽量减 ...

  8. 微信小程序服务器支付sdk,微信小程序之支付后如何调用SDK的异步通知

    微信小程序之支付后如何调用SDK的异步通知 发布时间:2021-07-05 10:47:33 来源:亿速云 阅读:57 作者:小新 这篇文章主要介绍微信小程序之支付后如何调用SDK的异步通知,文中介绍 ...

  9. C# 委托的三种调用示例(同步调用 异步调用 异步回调)

    首先,通过代码定义一个委托和下面三个示例将要调用的方法: 复制代码 代码如下: public delegate int AddHandler(int a,int b);     public clas ...

最新文章

  1. ibaits插入即获取主键(mssql,mysql,oracle)
  2. 如果在BackgroundWorker运行过程中关闭窗体…
  3. JavaScript数据运算
  4. 博客搬迁地址到csdn了
  5. History of pruning algorithm development and python implementation(finished)
  6. 在iOS8 下用Swift 创建自定义的键盘
  7. MYSQL的全表扫描,主键索引(聚集索引、第一索引),非主键索引(非聚集索引、第二索引),覆盖索引四种不同查询的分析...
  8. Java 8的默认方法:可以做什么和不能做什么?
  9. 关于设计模式的胡思乱想
  10. 【数学】Floating-Point Hazard
  11. Linux系统管理命令之管理文件和目录
  12. 项目管理表格模板/实用表格-项目启动
  13. 三星电脑打开虚拟服务器,三星笔记本进BIOS后怎么开启CPU虚拟化
  14. matlab解薛定谔方程,定态薛定谔方程的MATLAB求解(一).doc
  15. Dell T40服务器系统安装问题
  16. Android谷歌地图地理编码,使用谷歌地图api iOS反向地理编码
  17. C#使用CLE调用python
  18. 强化学习-学习笔记11 | 解决高估问题
  19. OpenCV切割大图(边界扩充,全黑图剔除)
  20. php mysql多线程处理数据6_PHP WIN MYSQL Rdeis 下多进程处理产品数据测试 31 万条 6 进程...

热门文章

  1. miui 系统组件 功能提示
  2. css实现子元素两头对齐
  3. 直流单臂桥的使用注意事项:
  4. vue2入门----父传子/子传父/兄弟相传
  5. Scala(四):对象
  6. ASR、TTS语音相关
  7. RADIUS Accounting
  8. 常用GIS行业技术架构总结-技术运维产品
  9. [Weex 学习]Weex Debug模式
  10. Android 跨应用发送自定义广播