• Using HttpClient properly to avoid CLOSE_WAIT TCP connections


             Apache的HttpComponent组件,用的人不在少数。但是能用好的人,却微乎其微,为什么?很简单,TCP里面的细节实现不是每个人都能捕获到的(细节是魔鬼),像并发请求控制&资源释放,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                    = "
    ";
    
        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;
            }
        }
    
    }
    


  • 相关阅读:
    vcruntime140.dll 14.0与PHP版本不兼容,PHP Warning: 'vcruntime140.dll' 14.0 is not compatible with this PHP build linked with 14.16 in Unknown on line 0
    PHP处理字符中的emoji表情
    Thinkphp5 使用unlink删除文件出错Permission denied
    TP5多字段排序
    TP5 按照汉字的拼音排序
    PHP发送微信模版消息
    [52ABP系列]
    [52ABP系列]
    通过微信公众号实现微信快捷登陆
    [Jexus系列] 一、安装并运行 Jexus
  • 原文地址:https://www.cnblogs.com/javawebsoa/p/3223818.html
Copyright © 2020-2023  润新知