Using HttpClient properly to avoid CLOSE_WAIT TCP connections

简介:          Apache的HttpComponent(这里是基于 version 4.1)组件,用的人不在少数。但是能用好的人,却微乎其微,为什么?很简单,TCP/IP协议里面的细节太多了(细节是魔鬼),像并发请求控制&资源释放,Nagle算法参数优化,Connection eviction,跟ulimit配对的total connection,重定向策略定制化,两类超时时间

         Apache的HttpComponent(这里是基于 version 4.1)组件,用的人不在少数。但是能用好的人,却微乎其微,为什么?很简单,TCP/IP协议里面的细节太多了(细节是魔鬼),像并发请求控制&资源释放,Nagle算法参数优化,Connection eviction,跟ulimit配对的total connection,重定向策略定制化,两类超时时间的合理设置,流读写等等。

         在最近的项目中,更是破天荒的遇到了close_wait问题,所以利用业余时间索性将之前同学写的HttpClient优化了一遍。下面我将贴出代码,如果大家发现了还有改进的余地,记得千万要留言知会我,共创最棒的代码:

/**
 * 史上最棒的HttpClient4封装,details please see
 * http://hc.apache.org/httpcomponents-client-ga/tutorial/html/index.html
 * 
 * @author von gosling 2013-5-7
 */
public class HttpClientManager {

    //Consider ulimit
    private static final int                   DEFAULT_MAX_TOTAL_CONNECTIONS     = 7500;
    //notice IE 6,7,8  
    private static final int                   DEFAULT_MAX_CONNECTIONS_PER_ROUTE = 200;

    private static final int                   DEFAULT_CONN_TIMEOUT_MILLISECONDS = 5 * 1000;

    private static final int                   DEFAULT_READ_TIMEOUT_MILLISECONDS = 60 * 1000;

    private static final int                   INIT_DELAY                        = 5 * 1000;

    private static final int                   CHECK_INTERVAL                    = 5 * 60 * 1000;

    private static String                      HTTP_REQUEST_ENCODING             = "UTF-8";
    private static String                      LINE_SEPARATOR                    = "\r\n";

    private static final Logger                LOG                               = LoggerFactory
                                                                                         .getLogger(HttpClientManager.class);

    private static ThreadSafeClientConnManager connectionManager;
    static {
        SchemeRegistry schemeRegistry = new SchemeRegistry();
        schemeRegistry.register(new Scheme("http", 80, PlainSocketFactory.getSocketFactory()));
        //schemeRegistry.register(new Scheme("https", 443, SSLSocketFactory.getSocketFactory()));

        connectionManager = new ThreadSafeClientConnManager(schemeRegistry);
        connectionManager.setMaxTotal(DEFAULT_MAX_TOTAL_CONNECTIONS);
        connectionManager.setDefaultMaxPerRoute(DEFAULT_MAX_CONNECTIONS_PER_ROUTE);

        //Connection eviction
        ScheduledExecutorService scheduledExeService = Executors.newScheduledThreadPool(1,
                new DaemonThreadFactory("Http-client-ConenctionPool-Monitor"));
        scheduledExeService.scheduleAtFixedRate(new IdleConnectionMonitor(connectionManager),
                INIT_DELAY, CHECK_INTERVAL, TimeUnit.MILLISECONDS);
    }

    public static String doPost(String reqURL, Map<String, String> params, String encoding,
                                Boolean enableSSL) {
        HttpClient httpClient = getHttpClient(enableSSL);

        String responseContent = "";
        try {
            HttpPost httpPost = buildHttpPostRequest(reqURL, params, encoding);
            HttpResponse response = httpClient.execute(httpPost);

            //            validateResponse(response, httpPost);

            HttpEntity entity = response.getEntity();
            if (entity != null) {
                // responseLength = entity.getContentLength();
                responseContent = EntityUtils.toString(entity, encoding);
                //Ensure that the entity content has been fully consumed and the underlying stream has been closed.
                EntityUtils.consume(entity);
            } else {
                LOG.warn("Http entity is null! request url is {},response status is {}", reqURL,
                        response.getStatusLine());
            }
        } catch (ConnectTimeoutException e) {
            LOG.warn(e.getMessage());
        } catch (SocketTimeoutException e) {
            LOG.warn("Read time out!");
        } catch (SSLPeerUnverifiedException e) {
            LOG.warn("Peer not authenticated!");
        } catch (Exception e) {
            LOG.error(e.getMessage(), e);
        }
        return responseContent;
    }

    public static String doPost(String reqURL, final String entities, String encoding) {
        HttpClient httpClient = getHttpClient(false);

        String responseContent = "";
        try {
            AbstractHttpEntity printWriterEntity = new AbstractHttpEntity() {
                public boolean isRepeatable() {
                    return false;
                }

                public long getContentLength() {
                    return -1;
                }

                public boolean isStreaming() {
                    return false;
                }

                public InputStream getContent() throws IOException {
                    // Should be implemented as well but is irrelevant for this case
                    throw new UnsupportedOperationException();
                }

                public void writeTo(final OutputStream outstream) throws IOException {
                    PrintWriter writer = new PrintWriter(new OutputStreamWriter(outstream,
                            HTTP_REQUEST_ENCODING));
                    writer.print(entities);
                    writer.print(LINE_SEPARATOR);
                    writer.flush();
                }

            };
            HttpPost httpPost = new HttpPost(reqURL);
            //If the data is large enough that you need to stream it,
            //you can write to a temp file and use FileEntity or possibly set up a pipe and use InputStreamEntity
            httpPost.setEntity(printWriterEntity);
            HttpResponse response = httpClient.execute(httpPost);

            validateResponse(response, httpPost);

            HttpEntity entity = response.getEntity();
            if (entity != null) {
                responseContent = EntityUtils.toString(entity, encoding);
                //Ensure that the entity content has been fully consumed and the underlying stream has been closed.
                EntityUtils.consume(entity);
            } else {
                LOG.warn("Http entity is null! request url is {},response status is {}", reqURL,
                        response.getStatusLine());
            }
        } catch (SocketTimeoutException e) {
            LOG.warn("Read time out!");
        } catch (SSLPeerUnverifiedException e) {
            LOG.warn("Peer not authenticated!");
        } catch (Exception e) {
            LOG.error(e.getMessage(), e);
        }
        return responseContent;
    }

    private static X509TrustManager customTrustManager(HttpClient httpClient) {
        //Trusting all certificates
        X509TrustManager xtm = new X509TrustManager() {
            public void checkClientTrusted(X509Certificate[] chain, String authType)
                    throws CertificateException {
            }

            public void checkServerTrusted(X509Certificate[] chain, String authType)
                    throws CertificateException {
            }

            public X509Certificate[] getAcceptedIssuers() {
                return null;
            }
        };
        try {
            SSLContext ctx = SSLContext.getInstance("TLS");
            if (null != ctx) {
                ctx.init(null, new TrustManager[] { xtm }, null);
                SSLSocketFactory socketFactory = new SSLSocketFactory(ctx,
                        SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
                httpClient.getConnectionManager().getSchemeRegistry()
                        .register(new Scheme("https", 443, socketFactory));
            }
        } catch (Exception e) {
            LOG.error(e.getMessage());
        }

        return xtm;
    }

    private static HttpClient getHttpClient(Boolean enableSSL) {
        DefaultHttpClient httpClient = new DefaultHttpClient(connectionManager);
        httpClient.setRedirectStrategy(new RedirectStrategy() { //设置重定向处理方式为自行处理
                    @Override
                    public boolean isRedirected(HttpRequest request, HttpResponse response,
                                                HttpContext context) throws ProtocolException {
                        return false;
                    }

                    @Override
                    public HttpUriRequest getRedirect(HttpRequest request, HttpResponse response,
                                                      HttpContext context) throws ProtocolException {
                        return null;
                    }
                });

        httpClient.getParams().setParameter(CoreConnectionPNames.SO_TIMEOUT,
                DEFAULT_READ_TIMEOUT_MILLISECONDS);
        httpClient.getParams().setParameter(CoreConnectionPNames.CONNECTION_TIMEOUT,
                DEFAULT_CONN_TIMEOUT_MILLISECONDS);
        //According to http use-case to decide to whether to open TCP_NODELAY option,So does SO_LINGER option 
        httpClient.getParams().setParameter(CoreConnectionPNames.TCP_NODELAY, Boolean.TRUE);
        httpClient.getParams().setParameter(CoreConnectionPNames.STALE_CONNECTION_CHECK,
                Boolean.FALSE);

        if (enableSSL) {
            customTrustManager(httpClient);
        }

        return httpClient;
    }

    public static Map.Entry<Integer, String> doGetHttpResponse(String url, String encoding) {
        HttpClient httpClient = getHttpClient(false);
        HttpGet httpget = new HttpGet(url);
        try {
            EncodingResponseHandler responseHandler = new EncodingResponseHandler();

            if (StringUtils.isBlank(encoding)) {
                encoding = HTTP_REQUEST_ENCODING;
            }
            responseHandler.setEncoding(encoding);

            return httpClient.execute(httpget, responseHandler);
        } catch (Exception e) {
            LOG.error(e.getMessage(), e);
        }
        return null;
    }

    public static String doGet(String url, String encoding) {
        Map.Entry<Integer, String> ret = doGetHttpResponse(url, encoding);
        if (ret == null) {
            return "";
        }
        if (ret.getKey() != HttpStatus.SC_OK) {
            LOG.error(
                    "Did not receive successful HTTP response: status code = {}, request url = {}",
                    ret.getKey(), url);
        }

        return ret.getValue();
    }

    public static void doPost(String url, Map<String, String> params) {
        HttpClient httpClient = getHttpClient(false);
        try {
            HttpPost httpPost = buildHttpPostRequest(url, params, HTTP.UTF_8);
            ResponseHandler<byte[]> handler = new ResponseHandler<byte[]>() {
                public byte[] handleResponse(HttpResponse response) throws ClientProtocolException,
                        IOException {
                    HttpEntity entity = response.getEntity();
                    if (entity != null) {
                        return EntityUtils.toByteArray(entity);
                    } else {
                        return null;
                    }
                }
            };
            httpClient.execute(httpPost, handler);
        } catch (Exception e) {
            LOG.error(e.getMessage(), e);
        }
    }

    private static HttpPost buildHttpPostRequest(String url, Map<String, String> params,
                                                 String encoding)
            throws UnsupportedEncodingException {
        HttpPost httpPost = new HttpPost(url);
        //Encode the form parameters
        if (!CollectionUtils.isEmpty(params)) {
            List<NameValuePair> nvps = Lists.newArrayList();
            Set<Entry<String, String>> paramEntrys = params.entrySet();
            for (Entry<String, String> entry : paramEntrys) {
                nvps.add(new BasicNameValuePair(entry.getKey(), entry.getValue()));
            }
            httpPost.setEntity(new UrlEncodedFormEntity(nvps, encoding));
        }
        return httpPost;
    }

    //    private static void validateResponse(HttpResponse response, HttpGet get) throws IOException {
    //        StatusLine status = response.getStatusLine();
    //        if (status.getStatusCode() >= HttpStatus.SC_MULTIPLE_CHOICES) {
    //            LOG.warn(
    //                    "Did not receive successful HTTP response: status code = {}, status message = {}",
    //                    status.getStatusCode(), status.getReasonPhrase());
    //            get.abort();
    //            return;
    //        }
    //    }

    private static void validateResponse(HttpResponse response, HttpPost post) throws IOException {
        StatusLine status = response.getStatusLine();
        if (status.getStatusCode() >= HttpStatus.SC_MULTIPLE_CHOICES) {
            LOG.warn(
                    "Did not receive successful HTTP response: status code = {}, status message = {}",
                    status.getStatusCode(), status.getReasonPhrase());
            post.abort();
            return;
        }
    }

}


目录
相关文章
|
自然语言处理 监控 搜索推荐
CAP 快速部署项目体验评测
在体验过程中,我选择了 RAG 模板,整体部署较为顺畅,CAP 平台的一键部署功能简化了配置步骤。但也遇到了环境依赖、模型加载速度和网络配置等挑战。性能测试显示响应速度较快,高并发时表现稳定。二次开发使用 Flask 和 Vue,调试顺利,功能正常运行。建议 CAP 增加 NLP、推荐系统、IoT 应用和开源项目集成等模板,以提升模板库的丰富度。
|
图形学 索引
Unity 之 三种抽奖示例
Unity做的三种抽奖的示例,根据需求修改动画时间和效果以及获取概率,文末附示例链接。
1294 0
Unity 之 三种抽奖示例
|
11月前
|
运维 Cloud Native 持续交付
深入理解云原生架构及其在现代企业中的应用
随着数字化转型的浪潮席卷全球,企业正面临着前所未有的挑战与机遇。云计算技术的迅猛发展,特别是云原生架构的兴起,正在重塑企业的IT基础设施和软件开发模式。本文将深入探讨云原生的核心概念、关键技术以及如何在企业中实施云原生策略,以实现更高效的资源利用和更快的市场响应速度。通过分析云原生架构的优势和面临的挑战,我们将揭示它如何助力企业在激烈的市场竞争中保持领先地位。
302 13
|
弹性计算 运维 自然语言处理
开发者评测|操作系统智能助手OS Copilot
OS Copilot 是阿里云针对Linux操作系统推出的一款智能助手,它利用大模型技术提供自然语言问答、辅助命令执行和系统运维调优等功能,旨在提高Alibaba Cloud Linux的使用效率。OS Copilot在新人上手时并不简单,文档指引不够清晰,存在一些步骤无法顺利执行的问题,比如环境配置和命令执行。此外,产品目前仅支持Alibaba Cloud Linux,限制了其应用范围。虽然OS Copilot在一些基本功能上表现尚可,如解答问题和编写简单脚本,但在处理复杂任务或自动化运维时显得不足。总体而言,OS Copilot对新手和阿里云环境有一定的帮助,但功能和使用体验仍有改进空间。
|
11月前
|
存储 人工智能 安全
云计算与网络安全:技术融合与挑战
在数字化时代的浪潮中,云计算和网络安全已成为推动社会进步的两大关键技术。本文将探讨云计算服务的发展,网络安全的重要性,以及信息安全技术的演进。我们将通过实例分析,揭示云服务如何增强数据保护,网络安全措施如何应对新兴威胁,以及信息安全技术的创新如何为企业带来竞争优势。文章旨在为读者提供对云计算和网络安全领域的深入理解,并展示它们如何共同塑造我们的未来。
|
人工智能 自然语言处理 数据挖掘
Claude 3.5:一场AI技术的惊艳飞跃 | AIGC
在这个科技日新月异的时代,人工智能(AI)的进步令人惊叹。博主体验了Claude 3.5 Sonnet的最新功能,对其卓越的性能、强大的内容创作与理解能力、创新的Artifacts功能、视觉理解与文本转录能力、革命性的“computeruse”功能、广泛的应用场景与兼容性以及成本效益和易用性深感震撼。这篇介绍将带你一窥其技术前沿的魅力。【10月更文挑战第12天】
517 1
|
人工智能 弹性计算 自然语言处理
解决方案应用实例 | 零售云业务中台+超级App,阿里云助力海底捞全面实现“云上捞”
2018年,海底捞和阿里云合作搭建数据中台、业务中台和移动中台的基础架构,并在此基础上升级了海底捞超级App,重构了会员体系。同年,海底捞在北京开设了第一家智慧餐厅,采用了自动配锅机、智能传菜机器人和智能厨房管理系统。2019年,海底捞的订餐排号系统搬上云端。到2020年,海底捞将自动配锅机和智能传菜机器人推向了数百家门店,同时,后勤行政系统也上云,从前端到后端所有核心业务系统全部上云,海底捞至此全面实现了“云上捞”。
6173 2
解决方案应用实例 | 零售云业务中台+超级App,阿里云助力海底捞全面实现“云上捞”
|
算法 大数据 数据安全/隐私保护
RSA加密:javax.crypto.IllegalBlockSizeException: Data must not be longer than 117 bytes
RSA加密:javax.crypto.IllegalBlockSizeException: Data must not be longer than 117 bytes
689 0
|
Java Linux 流计算
【极数系列】Flink环境搭建&Docker版本(04)
【极数系列】Flink环境搭建&Docker版本(04)
418 3
|
机器学习/深度学习 人工智能 数据挖掘
下一篇
oss云网关配置