在Java领域,谈到网络编程,可能大家脑海里第一反应就是MINA,NETTY,GRIZZLY等优秀的开源框架。没错,不过在深入探究这些框架之前,我们需要先从最original的技术探究开始(当然,需要大家先熟悉java.net.*类库)。这里,我要和大家分享一下HttpComponents项目的部分组件特性。HttpClient,想必大家早都接触过了吧。HttpComponents和HttpClient的”血缘“有点像guava和google-collection的关系。目前,HttpComponents已经是Apache的顶级项目了,它旨在为我们提供一个Http协议相关的Java平台工具集。它的代码组织很精妙,主要分两部分,一部分是核心工具集(包括HttpCore-bio,HttpCore-nio,HttpClient,HttpMIme,HttpCookie等),一部分是扩展工具集(目前主要包括ssl)
HttpClient主要包括Connection management,Status management,Authentication Management三部分。下面给出对它的二次封装,经过了线上的接近半年的验证(这里指的是httpClient 3,httpClient 4还有待检验),可以看做是一个高性能的Client封装吧。感兴趣的朋友可以根据apache的MPM IO模型进行部分参数的调整。
先来段httpClient 4的封装,代码如下:
[java] view plaincopyprint?
/**
* @author von gosling 2012-3-2
*/
public class HttpComponentsClientExecutor implements DisposableBean {
private static final int DEFAULT_MAX_TOTAL_CONNECTIONS = 100;
private static final int DEFAULT_MAX_CONNECTIONS_PER_ROUTE = 5; //notice IE 6,7,8
private static final int DEFAULT_CONN_TIMEOUT_MILLISECONDS = 5 * 1000;
private static final int DEFAULT_READ_TIMEOUT_MILLISECONDS = 60 * 1000;
private static final String HTTP_HEADER_CONTENT_ENCODING = "Content-Encoding";
private static final String ENCODING_GZIP = "gzip";
private HttpClient httpClient;
/**
* Create a new instance of the HttpComponentsClient with a default
* {@link HttpClient} that uses a default
* {@link org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager}.
*/
public HttpComponentsClientExecutor() {
SchemeRegistry schemeRegistry = new SchemeRegistry();
schemeRegistry.register(new Scheme("http", 80, PlainSocketFactory.getSocketFactory()));
schemeRegistry.register(new Scheme("https", 443, SSLSocketFactory.getSocketFactory()));
ThreadSafeClientConnManager connectionManager = new ThreadSafeClientConnManager(
schemeRegistry);
connectionManager.setMaxTotal(DEFAULT_MAX_TOTAL_CONNECTIONS);
connectionManager.setDefaultMaxPerRoute(DEFAULT_MAX_CONNECTIONS_PER_ROUTE);
this.httpClient = new DefaultHttpClient(connectionManager);
setConnectTimeout(DEFAULT_CONN_TIMEOUT_MILLISECONDS);
setReadTimeout(DEFAULT_READ_TIMEOUT_MILLISECONDS);
}
/**
* Create a new instance of the HttpComponentsClient with the given
* {@link HttpClient} instance.
*
* @param httpClient the HttpClient instance to use for this request
*/
public HttpComponentsClientExecutor(HttpClient httpClient) {
Validate.notNull(httpClient, "HttpClient must not be null");
//notice: if you want to custom exception recovery mechanism
//you should provide an implementation of the HttpRequestRetryHandler interface.
this.httpClient = httpClient;
}
/**
* Set the {@code HttpClient} used by this request.
*/
public void setHttpClient(HttpClient httpClient) {
this.httpClient = httpClient;
}
/**
* Return the {@code HttpClient} used by this request.
*/
public HttpClient getHttpClient() {
return this.httpClient;
}
/**
* Set the connection timeout for the underlying HttpClient. A timeout value
* of 0 specifies an infinite timeout.
*
* @param timeout the timeout value in milliseconds
*/
public void setConnectTimeout(int timeout) {
Validate.isTrue(timeout >= 0, "Timeout must be a non-negative value");
getHttpClient().getParams().setIntParameter(CoreConnectionPNames.CONNECTION_TIMEOUT,
timeout);
}
/**
* Set the socket timeout (SO_TIMEOUT) in milliseconds, which is the timeout
* for waiting for data or, put differently, a maximum period inactivity
* between two consecutive data packets.A timeout value of 0 specifies an
* infinite timeout.
*
* @param timeout the timeout value in milliseconds
*/
public void setReadTimeout(int timeout) {
Validate.isTrue(timeout >= 0, "Timeout must be a non-negative value");