使用异步HTTP提升客户端性能(HttpAsyncClient)

大家都知道,应用层的网络模型有同步、异步之分。

同步,意为着线程阻塞,只有等本次请求全部都完成了,才能进行下一次请求。
异步,好处是不阻塞当前线程,可以“万箭齐发”的将所有请求塞入缓冲区,然后谁的请求先完成就处理谁。

大家也注意到了,同步模式阻塞的只是“线程”。实际上,在异步模式流行之前,人们也经常用多线程的方式处理并发请求。然而,随着数据规模的不断加大,线程开销所带来的CPU、内存剧增,因此这种方法的应用比较有限。

近几年来,随着异步处理方案在node.js、Nginx等系统中的成功应用,异步模式的到了越来越多的关注。另外提一句:客户端与服务器端的异步处理是相互透明的,即允许客户端采用同步而服务器端采用异步。只是一般来说,异步的处理比同步要复杂许多。

下面回到实际问题上。
在近日的工作中,需要从Hadoop Job中调用一个Http计算服务以完成一些处理工作。我们使用了经典的HttpClient 3.x进行了实现。在一个HDFS文件分片上,性能数据大致是这样的:171237个文档、耗时305076ms。

备注:由于我们的Job跑在Hadoop上,在未来是会有N个Mapper同时运行,因此没有采用多线程的处理方式。

看看上面的数据,乍一看似乎还可以:平均每个文档的处理只需要1.8毫秒。然而从整个Map的角度来看,调用Http服务已经成为了整个Job的瓶颈,有必要进行一些优化。

在HttpClient进化到4.x后,官方提供了基于nio的异步版本:HttpAsyncClient。

这个异步版本的客户端,借助了Java并发库和nio进行封装,提供了非常方便的调用方式。

我们来看一下异步的代码:

Java

public void test2() throws InterruptedException, ExecutionException,
IOException {
CloseableHttpAsyncClient httpclient = HttpAsyncClients.createDefault();
// Start the client
httpclient.start();

// Execute 100 request in async
final HttpGet request = new HttpGet(
"http://xxxx");
request.setHeader("Connection", "close");
List<Future<HttpResponse>> respList = new LinkedList<Future<HttpResponse>>();
for (int i = 0; i < 50; i++) {
respList.add(httpclient.execute(request, null));
}

// Print response code
for (Future<HttpResponse> response : respList) {
response.get().getStatusLine();
// System.out.println(response.get().getStatusLine());
}

httpclient.close();
}

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

public void test2() throws InterruptedException, ExecutionException,

IOException {

CloseableHttpAsyncClient httpclient = HttpAsyncClients.createDefault();

// Start the client

httpclient.start();

// Execute 100 request in async

final HttpGet request = new HttpGet(

"http://xxxx");

request.setHeader("Connection", "close");

List<Future<HttpResponse>> respList = new LinkedList<Future<HttpResponse>>();

for (int i = 0; i < 50; i++) {

respList.add(httpclient.execute(request, null));

}

// Print response code

for (Future<HttpResponse> response : respList) {

response.get().getStatusLine();

// System.out.println(response.get().getStatusLine());

}

httpclient.close();

}

再来看一下同步的:

Java

public void test1() throws ClientProtocolException, IOException {
CloseableHttpClient httpclient = HttpClients.createDefault();

// Execute 500 request in async
for (int i = 0; i < 50; i++) {
HttpGet request = new HttpGet(
"http://xxxx");
request.setHeader("Connection", "close");
CloseableHttpResponse response = httpclient.execute(request);
// System.out.println(response.getStatusLine());
response.getStatusLine();
response.close();
}

httpclient.close();
}

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

public void test1() throws ClientProtocolException, IOException {

CloseableHttpClient httpclient = HttpClients.createDefault();

// Execute 500 request in async

for (int i = 0; i < 50; i++) {

HttpGet request = new HttpGet(

"http://xxxx");

request.setHeader("Connection", "close");

CloseableHttpResponse response = httpclient.execute(request);

// System.out.println(response.getStatusLine());

response.getStatusLine();

response.close();

}

httpclient.close();

}

不难发现,异步的代码使用了Future,使得最终的处理异常非常简单。

备注:我这里偷懒没有使用countdown latcher,所以future.get()实际还是会阻塞,但是发送http请求的client.execute阶段是非阻塞的。

下面来测一下性能。

我们采用了与Job中几乎相同的配置:每次batch发起50次请求,共50个batch。

结果如下:

Shell

async 2879 ms
sync 4190 ms

1

2

async 2879 ms

sync 4190 ms

使用异步请求的方式,比同步的时间节约了31%!

当然,尽管使用异步可以提升客户端调用的性能,但实际上是以提升并发为代价的,也就是latency和qps的关系。

换句话说,客户端异步没问题,但服务器端的性能必须跟的上,在我们的系统中,会通过控制batch的数量以及同时并发的mapper数量限制并发,以防止压垮服务器:-)

====2014.11.13 更新====

昨天忘记写如何获取返回的正文了,实际还是用Future返回的,补充如下:

Java

HttpResponse resp = respFuture.get();
InputStream input = resp.getEntity().getContent();

1

2

HttpResponse resp = respFuture.get();

InputStream input = resp.getEntity().getContent();

接下来,如何读取input的content就不再贴代码了。用IOUtil或者裸写BufferedInputStream/ByteArrayOutputStream都可以。

此外再补充下,实际线上效果比上面测试的要好,客户端大约节省了62%的时间开销。

时间: 2024-11-07 18:25:24

使用异步HTTP提升客户端性能(HttpAsyncClient)的相关文章

( 转)从四分钟到两秒——谈谈客户端性能优化的一些最佳实践

转:http://www.cnblogs.com/marvin/p/WinformOptimizSkill.html 背景 最近跟售后经理吃饭,他跟我再次谈起两年前为公司临时写的一个客户端,仍然非常激动的跟我说,这个客户端完爆了公司其他版本的客户端,包括最老的Delphi写的,Asp.Net写的,以及最新的Wpf写的客户端.无论是多么大的界面(集成的机房多),这个系统都是瞬间打开,而且运行非常稳定,一旦成功部署之后基本没有任何问题. 这个版本的客户端仅仅只是一个临时替代的版本:原来的Delphi

如何提升爬虫性能相关的知识点

如何提升爬虫性能相关的知识点 爬虫的本质是伪造socket客户端与服务端的通信过程,如果我们有多个url待爬取,只用一个线程且采用串行的方式执行,那只能等待爬取一个url结束后才能继续下一个,这样我们就会发现效率非常低. 原因:爬虫是一项IO密集型任务,遇到IO问题就会阻塞,CPU运行就会停滞,直到阻塞结束.那么在CPU等待组合结束的过程中,任务其实是呈现出卡住的状态.但是,如果在单线程下进行N个任务且都是纯计算的任务的话,那么该线程对cpu的利用率仍然会很高,所以单线程下串行多个计算密集型任务

极光开发者沙龙 之 移动应用性能优化实践 【一】旧酒新瓶——换个角度提升 App 性能与质量

旧酒新瓶--换个角度提升 App 性能与质量 主讲人:高亮亮 ---   饿了么移动技术部高级iOS工程师,负责饿了么商家版iOS APP开发,对架构和系统底层有深入研究,擅长移动性能分析,trouble shooting,iOS 逆向编程. 主讲时间:2017-05-26 主讲内容: 1.性能与质量概述: 2."新"技术概念的介绍与实践: 3.违反常规的"真理": 一.性能与质量概述: ? 应用分级以及与性能质量的关系 ? 根据设备特点设计提升方案 ? 结合主要业

[.net 面向对象程序设计进阶] (15) 缓存(Cache)(二) 利用缓存提升程序性能

[.net 面向对象程序设计进阶] (15) 缓存(Cache)(二) 利用缓存提升程序性能 本节导读: 上节说了缓存是以空间来换取时间的技术,介绍了客户端缓存和两种常用服务器缓布,本节主要介绍一种.NET中特别重要的缓布技术Cache.利用Cache提升程序性能. 1. 缓存Cache的命名空间 .NET中对缓存有两个命名空间 命名空间1:System.Web.Caching 命名空间2:System.Runtime.Caching 引用范围:这两个命名空间,都可以在Web和非WEB应用程序中

提升Web性能的8个技巧总结

提升Web性能的8个技巧总结 在互联网盛行的今天,越来越多的在线用户希望得到安全可靠并且快速的访问体验.针对Web网页过于膨胀以及第三脚本蚕食流量等问题,Radware向网站运营人员提出以下改进建议,帮助他们为用户提供最快最优质的访问体验. 1. 管理“页面膨胀” 页面大小与性能有着密切的关系.Radware最新电商性能“行业现状”报告显示,100强电商页面大小中位数达到了1492KB,比一年半之前增大了48%. 在研究报告里加载最快的10个页面中,页面包含的资源请求中位数为50个,页面大小中位

使用ab.exe监测100个并发/100次请求情况下同步/异步访问数据库的性能差异

ab.exe介绍 ab.exe是apache server的一个组件,用于监测并发请求,并显示监测数据 具体使用及下载地址请参考:http://www.cnblogs.com/gossip/p/4398784.html 本文的目的 通过webapi接口模拟100个并发请求下,同步和异步访问数据库的性能差异 创建数据库及数据 --创建表结构 CREATE TABLE dbo.[Cars] ( Id INT IDENTITY(1000,1) NOT NULL, Model NVARCHAR(50) 

Web 2.0应用客户端性能问题十大根源

原文 http://www.infoq.com/cn/news/2010/08/web-performance-root/ Web 2.0应用的推广为用户带来了全新的体验,同时也让开发人员更加关注客户端性能问题.最近,资深Web性能诊断专家.知名工具dynatrace的创始人之一Andreas Grabner根据自己的工作经验,总结了Web 2.0应用客户端性能问题十大根源,InfoQ中文站将这十个问题做了概括整理,供Web开发人员借鉴和思考. 1. IE中的CSS选择器(selector)运行

React爬坑秘籍(一)——提升渲染性能

React爬坑秘籍(一)——提升渲染性能 ##前言 来到腾讯实习后,有幸八月份开始了腾讯办公助手PC端的开发.因为办公助手主推的是移动端,所以导师也是大胆的让我们实习生来技术选型并开发,他来做code review.之前也学习过React,当然也是非常合适这一次的开发. 我会梳理这一个月来,自己对架构的思考过程和踩过的坑.当然这一切都不一定是最佳的,所以希望能有更多的建议和讨论. 例子所需库:Webpack.React.Immutable.其中Webpack用于前端构建,如果不清楚的同学可以看这

Android客户端性能优化(魅族资深工程师毫无保留奉献)

本文由魅族科技有限公司资深Android开发工程师degao(嵌入式企鹅圈原创团队成员)撰写,是degao在嵌入式企鹅圈发表的第一篇原创文章,毫无保留地总结分享其在领导魅族多个项目开发中的Android客户端性能优化经验,极具实践价值! 即日起,嵌入式企鹅圈将在之前五个专栏(Linux内核驱动情景分析.资源紧缺型SOC嵌入式架构设计.嵌入式交叉工具链及其应用.嵌入式设计和编程.微信硬件平台和物联网解决方案)新增Android开发专栏!更多Android.Linux.嵌入式和物联网原创技术分享敬请