美文网首页
HttpClient 的一次Timeout waiting fo

HttpClient 的一次Timeout waiting fo

作者: IT菜鸟学习 | 来源:发表于2018-12-07 17:45 被阅读0次

设置了defaultMaxPerRoute为20。maxTotal设置为100.

/**
     * 首先实例化一个连接池管理器,设置最大连接数、并发连接数
     *
     * @return
     */
    @Bean(name = "httpClientConnectionManager")
    public PoolingHttpClientConnectionManager getHttpClientConnectionManager() {
        PoolingHttpClientConnectionManager httpClientConnectionManager = new PoolingHttpClientConnectionManager(60, TimeUnit.SECONDS);
        //最大连接数
        httpClientConnectionManager.setMaxTotal(maxTotal);
        //并发数
        httpClientConnectionManager.setDefaultMaxPerRoute(defaultMaxPerRoute);
        // Validate connections after 1 sec of inactivity
        httpClientConnectionManager.setValidateAfterInactivity(validateAfterInactivity);
        return httpClientConnectionManager;
    }

HttpUtil工具类,主要负责关闭rsp

public class HttpUtil {

    private static Logger logger = LoggerFactory.getLogger(HttpUtil.class);

    /**
     * 转化为字符串
     *
     * @param resp          响应对象
     * @param encoding      编码
     * @return              返回处理结果
     * @throws ServiceException    http处理异常
     */
    public static String formTString(HttpResponse resp, String encoding) throws ServiceException {
        String body = "";
        try {
            if (resp.getEntity() != null) {
                // 按指定编码转换结果实体为String类型
                body = EntityUtils.toString(resp.getEntity(), encoding);
                logger.info(body);
            }else{//有可能是head请求
                body =resp.getStatusLine().toString();
                logger.info("返回实体为空NULL");
            }
            EntityUtils.consume(resp.getEntity());
        } catch (IOException e) {
            throw new ServiceException(ErrorCode.HTTP_ERR_DESC, ErrorCode.HTTP_ERR_CODE, "", "");
        }finally{
            close(resp);
        }
        return body;
    }

    /**
     * 尝试关闭response
     *
     * @param resp              HttpResponse对象
     */
    private static void close(HttpResponse resp) {
        try {
            if(resp == null) {
                return;
            }
            //如果CloseableHttpResponse 是resp的父类,则支持关闭
            if(CloseableHttpResponse.class.isAssignableFrom(resp.getClass())){
                ((CloseableHttpResponse)resp).close();
            }
        } catch (IOException e) {
            logger.error(e.getMessage());
        }
    }
}

封装的请求,这里当header的code为200的时候才会进入去执行result = HttpUtil.formTString(response, "UTF-8"); 这个方法中会关闭rsp。当code不等于200的时候,超过了20次就会抛出Timeout waiting for connection from pool错误。

    /**
     * 不带参数的get请求,如果状态码为200,则返回body,如果不为200,则返回null
     *
     * @param apiUrl 请求地址
     * @return body
     * @throws ServiceException ServiceException
     */
    public static String aiGet(String accessToken, String deviceSerial, String apiUrl, Map<String, Object> params) throws ServiceException {
        String result = null;
        // 声明 http get 请求
        HttpGet httpGet = new HttpGet(ISApiUrl.ISAPI_DOMAIN+apiUrl);
        // 装载配置信息
        httpGet.setConfig(serviceClient.config);
        //获取头信息
        Map<String, String> headers = getHeaders(accessToken, deviceSerial);
        for (Map.Entry<String, String> e : headers.entrySet()) {
            httpGet.addHeader(e.getKey(), e.getValue());
        }
        logger.info("请求HTTP_config:header==" + Arrays.toString(httpGet.getAllHeaders()) + "==map=");
        CloseableHttpResponse response = null;
        try {
            // 发起请求
            response = serviceClient.httpClient.execute(httpGet);
            response.getFirstHeader("Code");
            // 判断状态码是否为200
            if (response.getStatusLine().getStatusCode() == 200) {
                if (!"200".equals(response.getFirstHeader("Code").getValue())) {
                    throw new ServiceException(response.getFirstHeader("Message").getValue(), response.getFirstHeader("Code").getValue(), null, apiUrl);
                }
                // 返回响应体的内容
                //result = EntityUtils.toString(response.getEntity(), "UTF-8");
                  result = HttpUtil.formTString(response, "UTF-8");
            }

        } catch (IOException e) {
            logger.error(e.toString());
            logger.error("Http异常",e);
            throw new ServiceException(ErrorCode.HTTP_ERR_DESC, ErrorCode.HTTP_ERR_CODE, "", apiUrl);
        }
        return result;
    }

这里

result = HttpUtil.formTString(response, "UTF-8");

linux运行:

netstat -n | awk '/^tcp/ {++S[$NF]} END {for(a in S) print a, S[a]}'

[root@10 ~]# netstat -n | awk '/^tcp/ {++S[$NF]} END {for(a in S) print a, S[a]}'
TIME_WAIT 4
CLOSE_WAIT 22
ESTABLISHED 100

发现CLOSE_WAIT的数量始终在20以上,一直没降过。

关于TIME_WAIT和CLOSE_WAIT的区别和异常处理可以自行查找。

简单来说CLOSE_WAIT数目过大是由于被动关闭连接处理不当导致的。

我说一个场景,服务器A会去请求服务器B上面的apache获取文件资源,正常情况下,如果请求成功,那么在抓取完资源后服务器A会主动发出关闭连接的请求,这个时候就是主动关闭连接,连接状态我们可以看到是TIME_WAIT。如果一旦发生异常呢?假设请求的资源服务器B上并不存在,那么这个时候就会由服务器B发出关闭连接的请求,服务器A就是被动的关闭了连接,如果服务器A被动关闭连接之后自己并没有释放连接,那就会造成CLOSE_WAIT的状态了。

改后正确的

public static String aiGet(String accessToken, String deviceSerial, String apiUrl, Map<String, Object> params) throws ServiceException {
        String result = null;
        // 声明 http get 请求
        HttpGet httpGet = new HttpGet(ISApiUrl.ISAPI_DOMAIN+apiUrl);
        // 装载配置信息
        httpGet.setConfig(serviceClient.config);
        //获取头信息
        Map<String, String> headers = getHeaders(accessToken, deviceSerial);
        for (Map.Entry<String, String> e : headers.entrySet()) {
            httpGet.addHeader(e.getKey(), e.getValue());
        }
        logger.info("请求HTTP_config:header==" + Arrays.toString(httpGet.getAllHeaders()) + "==map=");
        CloseableHttpResponse response = null;
        try {
            // 发起请求
            response = serviceClient.httpClient.execute(httpGet);
            result = HttpUtil.formTString(response, "UTF-8");
            response.getFirstHeader("Code");
            // 判断状态码是否为200
            if (response.getStatusLine().getStatusCode() == 200) {
                if (!"200".equals(response.getFirstHeader("Code").getValue())) {
                    throw new ServiceException(response.getFirstHeader("Message").getValue(), response.getFirstHeader("Code").getValue(), null, apiUrl);
                }
                // 返回响应体的内容
                //result = EntityUtils.toString(response.getEntity(), "UTF-8");
            }

        } catch (IOException e) {
            logger.error(e.toString());
            logger.error("Http异常",e);
            throw new ServiceException(ErrorCode.HTTP_ERR_DESC, ErrorCode.HTTP_ERR_CODE, "", apiUrl);
        }
        return result;
    }

参考:https://my.oschina.net/fuxingCoder/blog/809835
https://blog.csdn.net/shootyou/article/details/6615051

相关文章

网友评论

      本文标题:HttpClient 的一次Timeout waiting fo

      本文链接:https://www.haomeiwen.com/subject/hidthqtx.html