zoukankan      html  css  js  c++  java
  • 基于线程池和连接池的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服务器有可能假死也,不要这么干!

  • 相关阅读:
    SpringCloud Alibaba微服务实战十
    万字长文!分布式锁的实现全都在这里了
    python编程中的小技巧(持续更新)
    工作十年的数据分析师被炒,没有方向,你根本躲不过中年危机
    github入门操作快速上手
    167. 两数之和 II
    167. 两数之和 II
    167. 两数之和 II
    怎么使用Fiddler进行抓包
    怎么使用Fiddler进行抓包
  • 原文地址:https://www.cnblogs.com/wscit/p/5768447.html
Copyright © 2011-2022 走看看