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                    = "\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;

        }

    }



}


 

 

你可能感兴趣的:(httpclient)