基于线程池和连接池的Http请求

背景:最新项目需求调用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服务器有可能假死也,不要这么干!

时间: 2024-10-12 13:52:06

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

缓冲池,线程池,连接池

SSH:[email protected]:unbelievableme/object-pool.git   HTTPS:https://github.com/unbelievableme/object-pool.git 缓冲池 设计要点:包含三个队列:空缓冲队列(emq),装满输入数据的输入的队列(inq),装满输出数据的输出队列(outq),输入程序包括收容输入(hin),提取输入(sin),输出程序包括收容输出(hout)和提取输出(sout). 注意点:输入程序和输出程序会对缓冲区并发访

mysql线程池与连接池

线程池与连接池 连接池通常实现在Client端,是指应用(客户端)创建预先创建一定的连接,利用这些连接服务于客户端所有的DB请求.如果某一个时刻,空闲的连接数小于DB的请求数,则需要将请求排队,等待空闲连接处理.通过连接池可以复用连接,避免连接的频繁创建和释放,从而减少请求的平均响应时间,并且在请求繁忙时,通过请求排队,可以缓冲应用对DB的冲击.线程池实现在server端,通过创建一定数量的线程服务DB请求,相对于one-conection-per-thread的一个线程服务一个连接的方式,线程

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

WebSphere 中池资源调优 - 线程池.连接池和 ORB 来自:https://www.ibm.com/developerworks/cn/websphere/library/techarticles/1106_zhuxl_websphereenhancement/1106_zhuxl_websphereenhancement.html IBM WebSphere Application Server (以下简称 WAS)能支持的应用程序越来越多,而这些应用程序有各自的独特特性.需求和服务

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

1.ProxySQL的线程 ProxySQL由多个模块组成,是一个多线程的daemon类程序.每个模块都有一个或多个线程去执行任务. 例如,以下是刚启动ProxySQL时的进程情况,一个main进程,一个主线程,21个线程. [[email protected] ~]# pstree | grep proxy |-proxysql---proxysql---21*[{proxysql}] 下面是正常运行时使用的线程列表: 1.1 Main thread 这其实是一个进程,该进程只负责引导.启动核

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

   thread_pool 和 connection_pool 当客户端请求的数据量比较大的时候,使用线程池可以节约大量的系统资源,使得更多的CPU时间和内存可以高效地利用起来.而数据库连接池的使用则将大大提高程序运行效率,同时,我们可以通过其自身的管理机制来监视数据库连接的数量.使用情况等.本文我们主要就介绍一下线程池和数据库连接池的原理,接下来我们一起来了解一下这一部分内容. 首先介绍什么是mysql thread pool,干什么用的?使用线程池主要可以达到以下两个目的:1.在大并发的时

网络协议 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次. >强烈依赖数据库的驱动jar 解决办法: Class.forName("com.mysql.jdbc.Driver"); 1.1.2 API详解:java.sql.Statement接口: 操作sql语句,并返回相应结果 String sql = "某SQL语句&qu

【C++基础知识】线程池、内存池、连接池的实现

//=============threadpool.c========== #include "threadpool.h" #include <stdio.h> pthread_mutex_t ThreadPool::mutex = PTHREAD_MUTEX_INITIALIZER; pthread_cond_t ThreadPool::cond = PTHREAD_COND_INITIALIZER; std::vector<Task*> ThreadPool

性能测试(九)连接池和线程

之前的博客中,有说的性能测试常见术语:连接池.其中大概简述了连接池的作用等,这篇博客,就介绍下连接池以及连接池中线程对象的原理,作用以及优点... 一.连接池 1.什么是连接池?我们为什么需要它? 连接池允许多个客户端使用缓存起来的连接对象,这些对象可以连接数据库,它们是共享的.可被重复使用的. 打开/关闭数据库连接开销很大,连接池技术允许我们在连接池里维护连接对象,这样可以提高数据库的执行命令的性能.多个客户端请求可以重复使用相同的连接对象,当每次收到一个客户端请求时, 就会搜索连接池,看看有

聊聊连接池和线程

一.连接池 1.什么是连接池?我们为什么需要它? 连接池允许多个客户端使用缓存起来的连接对象,这些对象可以连接数据库,它们是共享的.可被重复使用的. 打开/关闭数据库连接开销很大,连接池技术允许我们在连接池里维护连接对象,这样可以提高数据库的执行命令的性能.多个客户端请求可以重复使用相同的连接对象,当每次收到一个客户端请求时, 就会搜索连接池,看看有没有闲置连接对象.如果没有,要么所有的客户端请求都进入队列排队,要么在池中创建一个新的连接对象(这取决于池里已有多少个连接存在以及配置支持多少连接)