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;
}
} }

Using HttpClient properly to avoid CLOSE_WAIT TCP connections的更多相关文章

  1. Configure the max limit for concurrent TCP connections(转)

    To keep the TCP/IP stack from taking all resources on the computer, there are different parameters t ...

  2. 【转载】Configure the max limit for concurrent TCP connections

    转载地址:http://smallvoid.com/article/winnt-tcpip-max-limit.html To keep the TCP/IP stack from taking al ...

  3. Managing TCP Connections in Dynamic Spectrum Access Based Wireless LANs

    2010年IEEE Secon的一篇文章.当然了,应该是之前就写好了,发表过,还是直接投到Secon了呢?直接投的吧,Secon不接受已发表过的吧. 本文的着笔点:有线网与DSAN(启用了DSA特性的 ...

  4. FIN vs RST in TCP connections different

    question: The way I understand this, there are 2 ways to close TCP connection: send FIN flag send RS ...

  5. 从问题看本质: 研究TCP close_wait的内幕

    Socket应用服务器TomcatOSUbuntu  /* * @author: ahuaxuan * @date: 2010-4-30 */ 最近遇到的一个关于socket.close的问题,在某个 ...

  6. TCP之close_wait

    TCP之close_wait 浏览:3697次  出处信息 /* * @author: ahuaxuan * @date: 2010-4-30 */ 查看各状态连接数: netstat -n | aw ...

  7. 使用httpclient抓取时,netstat 发现很多time_wait连接

    http://wiki.apache.org/HttpComponents/FrequentlyAskedConnectionManagementQuestions 1. Connections in ...

  8. LINUX 中的 TCP/IP协议 参数详解

    Ipsysctl tutorial 1.0.4 Prev Chapter 3. IPv4 variable reference Next https://www.frozentux.net/ipsys ...

  9. IP, TCP, and HTTP--reference

    IP, TCP, and HTTP Issue #10 Syncing Data, March 2014 By Daniel Eggert When an app communicates with ...

随机推荐

  1. .Net程序员 Solr-5.3之旅 (二)Solr 安装

    阅读目录 引言 Solr5.3环境搭建 Solr5.3创建第一个Core 结尾 引言 一个糟糕的设计有好的表现形式,它会被判死缓,一个好的设计有糟糕的表现形式,它会被判死刑立即执行. 以上摘自一个设计 ...

  2. 一条insert语句批量插入多条记录

    一条insert语句批量插入多条记录 常见的insert语句,向数据库中,一条语句只能插入一条数据: insert into persons (id_p, lastname , firstName,  ...

  3. js页面跳转

    js方式的页面跳转1.window.location.href方式    <script language="javascript" type="text/java ...

  4. angularjs中的绑定策略“@”,“=”,“&”实例

    <!DOCTYPE html> <html lang="zh-CN" ng-app="app"> <head> <me ...

  5. 用Spring Boot零配置快速创建web项目(1)

    一.Spring Boot简介 Spring Boot是由Pivotal团队提供的全新框架,其设计目的是用来简化新Spring应用的初始搭建以及开发过程.该框架使用了特定的方式来进行配置,从而使开发人 ...

  6. static和const关键字的作用

    static关键字至少有下列n个作用: (1)函数体内static变量的作用范围为该函数体,不同于auto变量,该变量的内存只被分配一次,因此其值在下次调用时仍维持上次的值: (2)在模块内的stat ...

  7. STL-空间配置器(allocator)

    STL的空间配置器作为STL六大部件的重要组成部分,它总是隐藏在一切组件的背后.它主要负责动态空间的分配.释放等管理工作.整个STL的操作对象(所有的数值)都存放在容器之内,而容器一定需要配置空间以置 ...

  8. JavaScript中的內定物件與函式: arguments, callee, caller, this, apply(), call()

    arguments, caller, callee, this都是用在函式(function)內的特殊內定物件.而apply()及call()則是用來呼叫函式的不同作法. arguments可用來取得 ...

  9. phpcms V9 联动菜单的调用

    /*********************************** 通过id获取显示联动菜单的 顶级父类的名称* @param  $linkageid 联动菜单id* @param  $keyi ...

  10. bat命令中的变量声明及使用

    在bat文件中声明变量的方式如下: set xxx_variant_name=yyyyyyyyyyyy move D:\abc\efg\test.txt %xxx_variant_name%\test ...