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. Repeater的ItemDataBound 事件中e.Item.DataItem 的数据类型

    1.使用DataSet和DataTable绑定数据源时 DataRowView view = (DataRowView)e.Item.DataItem; 2.DataReader绑定数据源时 Syst ...

  2. osg for android (一) 简单几何物体的加载与显示

    1. 首先需要一个OSG for android的环境. (1).NDK 现在Eclipse 对NDK已经相当友好了,已经不需要另外cygwin的参与,具体可以参考 Android NDK开发篇(一) ...

  3. 关于C++中的虚拟继承的一些总结

    1.为什么要引入虚拟继承 虚拟继承是多重继承中特有的概念.虚拟基类是为解决多重继承而出现的.如:类D继承自类B1.B2,而类B1.B2都继承自类A,因此在类D中两次出现类A中的变量和函数.为了节省内存 ...

  4. python反射机制

    http://blog.163.com/yang_jianli/blog/static/161990006201382241223156/ http://www.jb51.net/article/54 ...

  5. 【创建型】Singleton模式

    单例模式可以说是所有23种设计模式中最为简单的一个,没有之一.其主要思想就是保证整个应用环境中,最多只会有一个对象的实例.类关系图参考如下: 在c++中,单例模式的实现,较为常用的实现方式一般为: n ...

  6. JSP网页防止sql注入攻击

    SQL注入攻击指的是通过构建特殊的输入作为参数传入Web应用程序,而这些输入大都是SQL语法里的一些组合,通过执行SQL语句进而执行攻击者所要的操作,其主要原因是程序没有细致地过滤用户输入的数据,致使 ...

  7. Android 之Service

    service是运行在后台的服务,你可以启动一个服务Service来播放音乐,或者记录你地理信息位置的改变,或者启动一个服务来运行并一直监听某种动作. 接下来分析一下service 的生命周期: 1: ...

  8. Angularjs Scope 原型链

    我们知道scope是可以继承的.scope的继承和js原型链是同一个概念. <div ng-controller="parentCtrl"> {{name}} < ...

  9. 常见MYSQL导入导出数据命令

    导出数据库: mysqldump –uuser -ppassword -hhost databasename > target_20150225.sql 打包: tar zcvf target_ ...

  10. 【转】DELL戴尔N4050笔记本拆机(图文)

    原文网址:http://www.ywxydn.com/1047.html