背景:最新项目需求调用http接口,所以打算使用最新的httpClient客户端写一个工具类,写好了以后在实际应用过程中遇到了一些问题,因为数据量还算

大,每次处理大概要处理600-700次请求,平均算下来大概需要20分钟,这个速度虽然是跑在定时任务中的,但是也是不能忍受的,所以有了这个博客.

1.首先想到的解决办法就是多线程发请求了,但是这个有坑,最后会在结果处说明.

2.代码方面如下

 ExecutorService executor = Executors.newFixedThreadPool(5);
FutureTask<Order> future;
for (TransactionRecord record:list) {
final String orderNo = record.getOrderNo();
future = new FutureTask<Order>(new OrderTask(orderNo));
executor.submit(future);
futureList.add(future);
} class OrderTask implements Callable<Order> {
private String orderNo;
public OrderTask(String orderNo) {
this.orderNo = orderNo;
}
@Override
public Order call() throws Exception {
Order order = orderService.getOrder(orderNo); //在getOrder中直接调用下面的我封装的http工具类
return order;
}
}

这是一段很简单的多线程代码,但是其中有一个坑需要大家注意的,不要在上面的循环中直接调用future.get()方法,如果直接调用的话就直接变成阻塞的了,和单线程

就没有区别了,可以自己写一个demo测试一下效率.

3.http方面的代码,可以全部贴出来,如下

   /**
* get
*/
public static HttpResultEntry doPost(String url, Map<String, String> params,
String charset) throws Exception {
HttpResultEntry resultEntry = new HttpResultEntry(); //自定义返回结果
CloseableHttpResponse response = null; //返回结果,释放链接
List<NameValuePair> pairs = new ArrayList<>();
; //参数
if (StringUtils.isBlank(url)) {
resultEntry.setMsg("请求地址异常");
resultEntry.setStatus(404);
resultEntry.setData("");
return resultEntry;
}
try {
if (params != null && !params.isEmpty()) {
for (Map.Entry<String, String> entry : params.entrySet()) {
String value = entry.getValue();
if (value != null) {
pairs.add(new BasicNameValuePair(entry.getKey(), value));
}
}
}
HttpPost httpPost = new HttpPost(url);
httpPost.setEntity(new UrlEncodedFormEntity(pairs, charset));
response = httpClient.execute(httpPost); //建立链接得到返回结果
int statusCode = response.getStatusLine().getStatusCode(); //返回的结果码
if (statusCode != 200) {
httpPost.abort();
resultEntry.setMsg("请求异常");
resultEntry.setStatus(statusCode);
resultEntry.setData("");
LOGGER.info("返回异常:{}", resultEntry);
return resultEntry;
}
HttpEntity httpEntity = response.getEntity();
String result = null;
if (httpEntity == null) {
resultEntry.setMsg("返回结果异常");
resultEntry.setStatus(statusCode);
resultEntry.setData("");
return resultEntry;
} else {
result = EntityUtils.toString(httpEntity, charset);
}
resultEntry.setMsg("请求正常");
resultEntry.setStatus(statusCode);
resultEntry.setData(result); EntityUtils.consume(httpEntity); //按照官方文档的说法:二者都释放了才可以正常的释放链接
response.close();
return resultEntry;
} catch (Exception e) {
LOGGER.error("请求错误:{},错误信息:{}", e.getMessage(), e);
throw new Exception("HTTP请求异常");
} finally {
if (response != null) {
try {
response.close();
} catch (IOException e) {
LOGGER.error("关闭流异常:{},错误信息:{}", e.getMessage(), e);
}
}
}
} /**
* post
*/
public static HttpResultEntry doGet(String url, Map<String, String> params,
String charset) throws Exception {
HttpResultEntry resultEntry = new HttpResultEntry(); //自定义返回结果
CloseableHttpResponse response = null; //返回结果,释放链接
List<NameValuePair> pairs = new ArrayList<>();//参数
if (StringUtils.isBlank(url)) {
resultEntry.setMsg("请求地址异常");
resultEntry.setStatus(404);
resultEntry.setData("");
return resultEntry;
}
try {
if (params != null && !params.isEmpty()) {
for (Map.Entry<String, String> entry : params.entrySet()) {
String value = entry.getValue();
if (value != null) {
pairs.add(new BasicNameValuePair(entry.getKey(), value));
}
}
url += "?" + EntityUtils.toString(new UrlEncodedFormEntity(pairs, charset));
}
HttpGet httpGet = new HttpGet(url);
response = httpClient.execute(httpGet); //建立链接得到返回结果
int statusCode = response.getStatusLine().getStatusCode();
if (statusCode != 200) {
httpGet.abort();
resultEntry.setMsg("请求异常");
resultEntry.setStatus(statusCode);
resultEntry.setData("");
LOGGER.info("返回异常:{}", resultEntry);
return resultEntry;
}
HttpEntity httpEntity = response.getEntity();
String result = null;
if (httpEntity == null) {
resultEntry.setMsg("返回结果异常");
resultEntry.setStatus(statusCode);
resultEntry.setData("");
return resultEntry;
} else {
result = EntityUtils.toString(httpEntity, charset);
}
resultEntry.setMsg("请求正常");
resultEntry.setStatus(statusCode);
resultEntry.setData(result); EntityUtils.consume(httpEntity); //按照官方文档的说法:二者都释放了才可以正常的释放链接
response.close();
return resultEntry;
} catch (Exception e) {
LOGGER.error("请求错误:{},错误信息:{}", e.getMessage(), e);
throw new Exception("HTTP请求异常");
} finally {
if (response != null) {
try {
response.close();
} catch (IOException e) {
LOGGER.error("关闭流异常:{},错误信息:{}", e.getMessage(), e);
}
}
}
}

使用的http连接池,连接池的代码很简单就不粘贴了,首先使用的时候一定要注意最后的释放工作,必须把httpEntry和respose都释放掉,按照官方文档的说法,只有这样才是真的释放了链接的,也就是这个链接才可以被复用.

总结:需要特别注意的是,访问别人的http接口的时候一定不要开太多的线程,免得把别人的接口搞挂了,想我就的到了教训,我在访问一个http的接口的时候开了一百个线程,666次请求跑了3.7秒左右,是很快我也很开心,然后那边数据库受不了压力了,导致报警最后直接开了白名单,尴尬了,所以使用这些的时候一定要考虑这些,开三五个就够了,另外如果开太多线程的话tomcat服务器有可能假死也,不要这么干!

基于线程池和连接池的Http请求的更多相关文章

  1. (转)WebSphere 中池资源调优 - 线程池、连接池和 ORB

    WebSphere 中池资源调优 - 线程池.连接池和 ORB 来自:https://www.ibm.com/developerworks/cn/websphere/library/techartic ...

  2. commons-pool与commons-pool2连接池(Hadoop连接池)

    commons-pool和commons-pool2是用来建立对象池的框架,提供了一些将对象池化必须要实现的接口和一些默认动作.对象池化之后可以通过pool的概念去管理其生命周期,例如对象的创建,使用 ...

  3. JDBC连接池-自定义连接池

    JDBC连接池 java JDBC连接中用到Connection   在每次对数据进行增删查改 都要 开启  .关闭  ,在实例开发项目中 ,浪费了很大的资源 ,以下是之前连接JDBC的案例 pack ...

  4. MySQL中间件之ProxySQL(5):线程、线程池、连接池

    返回ProxySQL系列文章:http://www.cnblogs.com/f-ck-need-u/p/7586194.html 1.ProxySQL的线程 ProxySQL由多个模块组成,是一个多线 ...

  5. ProxySQL(5):线程、线程池、连接池

    文章转载自:https://www.cnblogs.com/f-ck-need-u/p/9281909.html ProxySQL的线程 ProxySQL由多个模块组成,是一个多线程的daemon类程 ...

  6. Mysql线程池系列一:什么是线程池和连接池( thread_pool 和 connection_pool)

       thread_pool 和 connection_pool 当客户端请求的数据量比较大的时候,使用线程池可以节约大量的系统资源,使得更多的CPU时间和内存可以高效地利用起来.而数据库连接池的使用 ...

  7. C# 基于创建一个mysql 连接池

    创建一个连接池操作类 using MySql.Data.MySqlClient; using System; using System.Collections.Generic; using Syste ...

  8. psycopg2.pool – Connections pooling / psycopg2.pool – 连接池 / postgresql 连接池

    创建新的PostgreSQL连接可以是一个昂贵的操作.这个模块提供了一些纯Python类直接在客户端应用程序实现简单的连接池.      class psycopg2.pool.AbstractCon ...

  9. 网络协议 finally{ return问题 注入问题 jdbc注册驱动问题 PreparedStatement 连接池目的 1.2.1DBCP连接池 C3P0连接池 MYSQL两种方式进行实物管理 JDBC事务 DBUtils事务 ThreadLocal 事务特性 并发访问 隔离级别

    1.1.1 API详解:注册驱动 DriverManager.registerDriver(new com.mysql.jdbc.Driver());不建议使用 原因有2个: >导致驱动被注册2 ...

随机推荐

  1. Xamarin Android 真机调试时闪退

    方法1:引起此问题的原因一般是因为 Mono Shared Runtime 在手机上没有运行,这个程序相当于.net运行时,没有运行的话用C#开发的程序自然无法运行. 解决方法是将此程序设置为自动运行 ...

  2. block(代码块)的介绍以及使用方法和变量之间的关系

    http://blog.csdn.net/menxu_work/article/details/8762848 block(代码块)的介绍以及使用方法和变量之间的关系 block(代码块)的介绍以及使 ...

  3. JS 的trim()

    去除字符串左右两端的空格,在vbscript里 可  用 trim.ltrim 或 rtrim,但 js 却没有这 3个 内置方法,需 手工编写.下面的实现方法  用到 正则表达式,效率不错, 把 三 ...

  4. iOS:搭建本地的服务器

    一.介绍 作为一个专业的程序员,不管你是前端还是移动端或者是后台,能够自己试着搭建一个本地的服务器还是很有必要的,有的时候,我们可以自己测试一些数据,很方便开发.其实,mac是自带有本地的服务器的,用 ...

  5. iOS:Xcode8以下真机测试iOS10.0和iOS10.1配置包

    一.介绍 xcode的升级都已经到8系列了,可是还是有很多开发者使用的xcode还是7系列,然而xcode7...最多支持9.3,无法给升级到10.0和10.1的iPhone手机用户进行真机测试.此时 ...

  6. js asp.net获取时间戳

    方法1: var timestamp = (new Date()).valueOf(); 结果:1280977330748 方法2: var timestamp=new Date().getTime( ...

  7. Objective-C语言分类与协议

    分类(Category)允许向一个类文件中添加新的方法声明,它不需要使用子类机制,并且在类实现的文件中的同一个名字下定义这些方法.其语法举例如下: #import "ClassName.h& ...

  8. Unity本身制作keystore

    网上有不少利用jdk生成keystore的介绍.本身复杂不容易理解.作为一个Unity游戏开发者,制作keystore不需要这么麻烦.应为Unity本身就自带了制作keystore的功能.下面介绍制作 ...

  9. DNS Prefetch

    DNS 实现域名到IP的映射.通过域名访问站点,每次请求都要做DNS解析.目前每次DNS解析,通常在200ms以下.针对DNS解析耗时问题,一些浏览器通过DNS Prefetch 来提高访问的流畅性. ...

  10. java ftp下载文件

    1.使用官方正规的jar commons-net-1.4.1.jar jakarta-oro-2.0.8.jar 注意:使用ftp从windows服务器下载文件和从linux服务器下载文件不一样 2. ...