高并发下,自己写的基于httpclient发送post请求的工具类,求高手分析代码中的问题

ahdkk 发布于 2016/09/29 15:16
阅读 4K+
收藏 1

基于httpclient4.5.1写了一个发送http请求的工具类:

目前是每秒发送2000的post请求,服务器是一个apache服务器,

出现的问题有如下:

1.可能存在内存溢出,可能是httpclient没有关闭导致的,我使用的是单例,我觉得不需要每次都关闭打开,这样太浪费资源了,所以这个内存溢出我不知道该怎么解决

2.服务器端在客户端跑起来运行几秒之后,用命令查看出现了大量的处于TIME_WAIT状态的tcp连接,都是源于80端口的,客户端 会间断的报错:java.net.NoRouteToHostException: Cannot assign requested address. (这个问题我分析是因为 tcp还没有从TIME_WAIT到CLOSED,TIME_WAIT的tcp连接满了,导致新的请求无法建立tcp连接)这个我不知道如何立刻马上关闭连接,求指导。

代码如下:

    private static CloseableHttpClient httpClient = null;


    static{
        cm = new PoolingHttpClientConnectionManager();
        // 将最大连接数增加到
        cm.setMaxTotal(Integer.valueOf(Config.getProperty("maxTotal")));
//            cm.setMaxTotal(12);
        // 将每个路由基础的连接增加到
        cm.setDefaultMaxPerRoute(Integer.valueOf(Config.getProperty("maxPerRoute")));
//            cm.setDefaultMaxPerRoute(2);
        //将目标主机的最大连接数增加到
        //HttpHost host = new HttpHost(Config.getProperty("remote_host"), Integer.valueOf(Config.getProperty("remote_host_port")));
        //cm.setMaxPerRoute(new HttpRoute(host), Integer.valueOf(Config.getProperty("remote_maxPerRoute")));
        httpClient = HttpClients.custom().setConnectionManager(cm).build();
        logger.error("初始化HttpClients");
    }


    /**
     * 通过连接池获取HttpClient
     * @return
     */
    public static CloseableHttpClient getHttpClient(){
        return httpClient;
    }
 
 //发送post请求
 public static String httpPostRequest(String url, Map<String, Object> params) throws UnsupportedEncodingException{
        HttpPost httpPost = new HttpPost(url);
        ArrayList<NameValuePair> pairs = covertParams2NVPS(params);
        httpPost.setEntity(new UrlEncodedFormEntity(pairs, UTF_8));
        return getResult(httpPost);
    }
 
 //参数转换
 private static ArrayList<NameValuePair> covertParams2NVPS(Map<String, Object> params){
        ArrayList<NameValuePair> pairs = new ArrayList<NameValuePair>();
        for (Map.Entry<String, Object> param: params.entrySet()) {
            pairs.add(new BasicNameValuePair(param.getKey(), (String) param.getValue()));
        }


        return pairs;
    }




    /**
     * 通用处理Http请求:post和get在最终都会走这里
     * @param request
     * @return
     */
    private static String getResult(HttpRequestBase request){
        CloseableHttpClient httpClient = HttpClientUtils.getHttpClient();
        CloseableHttpResponse response = null;
        try{
            response = httpClient.execute(request);
            HttpEntity entity = response.getEntity();
            if(entity!=null){
                String result = EntityUtils.toString(entity);
                return result;
            }
        }catch(ClientProtocolException e){
            logger.error(e.getMessage(),e);
        }catch(IOException e){
            logger.error(e.getMessage(),e);
        }finally{
            if(response!=null){
                try {
                    response.close();
                } catch (IOException e) {
                    logger.error(e.getMessage(),e);
                }
            }
        }


        return EMPTY_STR;
    }

求大神指导,小白请走开

加载中
0
Rcd
Rcd

1. httpclient 维护连接池,在server发送FIN包时如果连接仍然在池中,则client只发送ACK而不发送FIN,此时导致server处于FIN-WAIT-2,客户端处于CLOSE-WAIT,抓包即可看到。

2. 如果是上述描述情况,只需增加一个IdleConnectionMonitor,监控idle和expired connection,从池中移除,则client就会发送FIN包,可以使得关闭流程走完。

3. 相关代码可以参考官方: https://hc.apache.org/httpcomponents-client-ga/tutorial/html/connmgmt.html  连接中的 2.5章节,已经实践过,可以很好工作。

ahdkk
ahdkk
回复 @Rcd : 国庆节后能加个QQ聊一下不? 1003943267
ahdkk
ahdkk
回复 @Rcd : 我第一次用,不要笑啊,我感觉PoolingHttpClientConnectionManager就是连接池,我的httpclient就是从这个中拿到的
Rcd
Rcd
回复 @ahdkk : 下列code中并没有使用池,建议使用,关闭握手问题是看谁先发出的FIN信号,并没有固定哪个状态一定在哪端,只要哪端出现大量CLOSE-WAIT,哪端就有问题。修改OS net参数只能缓解,并没有解决问题。这种问题抓包即可,很容易分析,出现大量的TIME-WAIT则说明使用短连接,非长连接。
ahdkk
ahdkk
我的问题搞定了,但是还有一些疑问,我放到了我自己的回答中了,欢迎大神指教
0
skhuhu
skhuhu
这端时间也在做httpclient , 你这种情况我在查阅官方文档的时候看过,官方文档中提到过 消费这个词 连接池怎么触发他关闭, 是通过消费response.getEntity().getContent() 这个inputstream , 本人也在做这方面的研究···只是给楼主一个建议···不一定对···如果楼主解决了 ·请在贴中说明下 ···谢谢
ahdkk
ahdkk
我的问题搞定了,但是还有一些疑问,我放到了我自己的回答中了
ahdkk
ahdkk
如果我解决了就写一个博客,大家共享
0
ahdkk
ahdkk
首先我参考了apache的文档确定我的代码没什么问题,我的问题解决了,但不是通过代码解决的而是通过修改Linux的内核参数搞定的,缩短TCP的关闭时间。
目前尚存疑问:
1. 我看过TCP连接情况,客户端和服务器端都有大量的TIME_WAIT的连接没有关闭,客户端有我可以理解,服务器端有我理解不了,按照TCP的四次握手关闭流程中服务器端不可能出现TIME_WAIT的,但我这里出现了,很奇怪。
2. 我用的是HttpClient 走的是http1.1协议,按照协议的说明http使用的tcp是持久化的也就是建立一个tcp连接可以被多个http请求使用,但是我这里却出现了每次请求都新建tcp连接的情况。如何强制使用已有的tcp传输?稍后我把代码全部放到这里供大家看看,分析一下,欢迎指教。
0
ahdkk
ahdkk

以下是我的代码:

public class HttpClientUtils { public static Logger logger = Logger.getLogger(HttpClientUtils.class); private static PoolingHttpClientConnectionManager cm = null; private static String EMPTY_STR = ""; private static String UTF_8 = "UTF-8"; private static CloseableHttpClient httpClient = null; static{ cm = new PoolingHttpClientConnectionManager(); // 将最大连接数增加到  cm.setMaxTotal(Integer.valueOf(Config.getProperty("maxTotal"))); // 将每个路由基础的连接增加到  cm.setDefaultMaxPerRoute(Integer.valueOf(Config.getProperty("maxPerRoute"))); httpClient = HttpClients.custom().setConnectionManager(cm).build(); logger.info("初始化HttpClients");
    } /**  * 通过连接池获取HttpClient--单例的  * @return  */  public static CloseableHttpClient getHttpClient(){ return httpClient;
    } /**  * 获得连接池管理器--单例的  * @return  */  public static PoolingHttpClientConnectionManager getHttpClientManager(){ return cm;
    } /**  *  * @param url  * @return  */  public static String httpGetRequest(String url){
        HttpGet httpGet = new HttpGet(url); return getResult(httpGet);
    } public static String httpGetRequest(String url, Map<String, Object> params) throws URISyntaxException{
        URIBuilder ub = new URIBuilder();
        ub.setPath(url);

        ArrayList<NameValuePair> pairs = covertParams2NVPS(params);
        ub.setParameters(pairs);

        HttpGet httpGet = new HttpGet(ub.build()); return getResult(httpGet);
    } public static String httpGetRequest(String url, Map<String, Object> headers,
                                        Map<String, Object> params) throws URISyntaxException{
        URIBuilder ub = new URIBuilder();
        ub.setPath(url);

        ArrayList<NameValuePair> pairs = covertParams2NVPS(params);
        ub.setParameters(pairs);

        HttpGet httpGet = new HttpGet(ub.build()); for (Map.Entry<String, Object> param: headers.entrySet()) {
            httpGet.addHeader(param.getKey(), (String) param.getValue());
        } return getResult(httpGet);
    } public static String httpPostRequest(String url){
        HttpPost httpPost = new HttpPost(url); return getResult(httpPost);
    } public static String httpPostRequest(String url, Map<String, Object> params) throws UnsupportedEncodingException{
        HttpPost httpPost = new HttpPost(url);
        ArrayList<NameValuePair> pairs = covertParams2NVPS(params);
        httpPost.setEntity(new UrlEncodedFormEntity(pairs, UTF_8)); return getResult(httpPost);
    } public static String httpPostRequest(String url, Map<String, Object> headers,
                                         Map<String, Object> params) throws UnsupportedEncodingException{
        HttpPost httpPost = new HttpPost(url); for (Map.Entry<String, Object> param: headers.entrySet()) {
            httpPost.addHeader(param.getKey(), (String) param.getValue());
        }

        ArrayList<NameValuePair> pairs = covertParams2NVPS(params);
        httpPost.setEntity(new UrlEncodedFormEntity(pairs, UTF_8)); return getResult(httpPost);
    } private static ArrayList<NameValuePair> covertParams2NVPS(Map<String, Object> params){
        ArrayList<NameValuePair> pairs = new ArrayList<NameValuePair>(); for (Map.Entry<String, Object> param: params.entrySet()) {
            pairs.add(new BasicNameValuePair(param.getKey(), (String) param.getValue()));
        } return pairs;
    } /**  * 处理Http请求  * @param request  * @return  */  private static String getResult(HttpRequestBase request){
        CloseableHttpClient httpClient = HttpClientUtils.getHttpClient();
        CloseableHttpResponse response = null; try{
            response = httpClient.execute(request);
            HttpEntity entity = response.getEntity(); if(entity!=null){
                String result = EntityUtils.toString(entity); return result;
            }
        }catch(ClientProtocolException e){ logger.error(e.getMessage(),e);
        }catch(IOException e){ logger.error(e.getMessage(),e);
        }finally{ if(response!=null){ try {
                    response.close();
                } catch (IOException e) { logger.error(e.getMessage(),e);
                }
            }
        } return EMPTY_STR;
    }
}

返回顶部
顶部