基於線程池和連接池的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服務器有可能假死也,不要這么干!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM