1、获取接口定义【自定义RequestHttp】
作用:生成(获取)http请求客户端
1.1获取httpClient 2.获取httpProxy
public interface RequestHttp<H, P> {
/**
* 返回httpClient.CloseableHttpClient是apache的一个类
*
* @return 返回httpClient
*/
H getRequestHttpClient();
/**
* 返回httpProxy.HttpHost 是apache的一个类
* @return 返回httpProxy
*/
P getRequestHttpProxy();
/**
* 返回HttpType.
*
*JODD_HTTP
Jodd提供一个很轻量级、原生的的http客户端,使用起来很简单、方便。它能很方便的发送和接收http消息
HttpClient
Apache Jakarta Common下的子项目,用来提供高效的、最新的、功能丰富的支持HTTP协议的客户端编程工具包,并且它支持HTTP协议最新的版本和建议
OK_HTTP
一个处理网络请求的开源项目,是安卓端最火热的轻量级框架,由移动支付Square公司贡献(该公司还贡献了Picasso) 用于替代HttpUrlConnection和Apache HttpClient
*/
HttpType getRequestType();
}
1.2、接口实现
属性:
private CloseableHttpClient httpClient;
private HttpHost httpProxy;
方法:
public void initHttp() {
//首先、从内存中获取
WxMpConfigStorage configStorage = this.getWxMpConfigStorage();
ApacheHttpClientBuilder apacheHttpClientBuilder = configStorage.getApacheHttpClientBuilder();
//其次、使用默认方式创建
if (null == apacheHttpClientBuilder) {
apacheHttpClientBuilder = DefaultApacheHttpClientBuilder.get();
}
//最后、设置代理
apacheHttpClientBuilder.httpProxyHost(configStorage.getHttpProxyHost())
.httpProxyPort(configStorage.getHttpProxyPort())
.httpProxyUsername(configStorage.getHttpProxyUsername())
.httpProxyPassword(configStorage.getHttpProxyPassword());
if (configStorage.getHttpProxyHost() != null && configStorage.getHttpProxyPort() > 0) {
this.httpProxy = new HttpHost(configStorage.getHttpProxyHost(), configStorage.getHttpProxyPort());
}
this.httpClient = apacheHttpClientBuilder.build();
}
1.3、httpClient 管理器
具体创建【httpclient 连接管理器】 DefaultApacheHttpClientBuilder
public interface ApacheHttpClientBuilder {
/**
* 构建httpclient实例.
*
* @return new instance of CloseableHttpClient
*/
CloseableHttpClient build();
/**
* 代理服务器地址.
*/
ApacheHttpClientBuilder httpProxyHost(String httpProxyHost);
/**
* 代理服务器端口.
*/
ApacheHttpClientBuilder httpProxyPort(int httpProxyPort);
/**
* 代理服务器用户名.
*/
ApacheHttpClientBuilder httpProxyUsername(String httpProxyUsername);
/**
* 代理服务器密码.
*/
ApacheHttpClientBuilder httpProxyPassword(String httpProxyPassword);
/**
* ssl连接socket工厂.
*/
ApacheHttpClientBuilder sslConnectionSocketFactory(SSLConnectionSocketFactory sslConnectionSocketFactory);
}
1.4、httpClient 管理器实现类
1、获取
// 单例模式,并持有唯一的CloseableHttpClient(仅首次调用创建)
public static DefaultApacheHttpClientBuilder get() {
return DefaultApacheHttpClientBuilder.SingletonHolder.INSTANCE;
}
// 内部类
private static class SingletonHolder {
private static final DefaultApacheHttpClientBuilder INSTANCE = new
DefaultApacheHttpClientBuilder();
}
2.空闲连接控制器
public static class IdleConnectionMonitorThread extends Thread {
private final HttpClientConnectionManager connMgr;
private final int idleConnTimeout;
private final int checkWaitTime;
//是否停止
private volatile boolean shutdown;
public IdleConnectionMonitorThread(HttpClientConnectionManager connMgr, int idleConnTimeout, int checkWaitTime) {
super("IdleConnectionMonitorThread");
this.connMgr = connMgr;
this.idleConnTimeout = idleConnTimeout;
this.checkWaitTime = checkWaitTime;
}
@Override
public void run() {
try {
while (!this.shutdown) {
synchronized (this) {
wait(this.checkWaitTime);
this.connMgr.closeExpiredConnections();
this.connMgr.closeIdleConnections(this.idleConnTimeout,
TimeUnit.MILLISECONDS);
}
}
} catch (InterruptedException ignore) {
}
}
public void trigger() {
synchronized (this) {
notifyAll();
}
}
public void shutdown() {
this.shutdown = true;
synchronized (this) {
notifyAll();
}
}
}
1.5.Http连接池
1、降低延迟:如果不采用连接池,每次连接发起Http请求的时候都会重新建立TCP连接(经历3次握手),用完就会关闭连接(4次挥手),如果采用连接池则减少了这部分时间损耗,别小看这几次握手,本人经过测试发现,基本上3倍的时间延迟
2、支持更大的并发:如果不采用连接池,每次连接都会打开一个端口,在大并发的情况下系统的端口资源很快就会被用完,导致无法建立新的连接
1.5.1代码
1、HttpConnectionManager.java连接池管理类,支持https协议
@Component
public class HttpConnectionManager {
PoolingHttpClientConnectionManager cm = null;
@PostConstruct
public void init() {
LayeredConnectionSocketFactory sslsf = null;
try {
sslsf = new SSLConnectionSocketFactory(SSLContext.getDefault());
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
Registry<ConnectionSocketFactory> socketFactoryRegistry = RegistryBuilder.<ConnectionSocketFactory> create()
.register("https", sslsf)
.register("http", new PlainConnectionSocketFactory())
.build();
cm =new PoolingHttpClientConnectionManager(socketFactoryRegistry);
cm.setMaxTotal(200);
cm.setDefaultMaxPerRoute(20);
}
public CloseableHttpClient getHttpClient() {
CloseableHttpClient httpClient = HttpClients.custom()
.setConnectionManager(cm)
.build();
/*CloseableHttpClient httpClient = HttpClients.createDefault();//如果不采用连接池就是这种方式获取连接*/
return httpClient;
}
}
1.5.2、连接池消费类:HaoMaiClient.java
@Component
public class HaoMaiClient {
@Autowired
HttpConnectionManager connManager;
public <T> T get(String path,Class<T> clazz){
CloseableHttpClient httpClient=connManager.getHttpClient();
HttpGet httpget = new HttpGet(path);
String json=null;
CloseableHttpResponse response=null;
try {
response = httpClient.execute(httpget);
InputStream in=response.getEntity().getContent();
json=IOUtils.toString(in);
in.close();
} catch (UnsupportedOperationException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally {
if(response!=null){
try {
response.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return JSON.parseObject(json, clazz);
}
}
1.5.3、原理及注意事项
连接池中连接都是在发起请求的时候建立,并且都是长连接
HaoMaiClient.java中的in.close();作用就是将用完的连接释放,下次请求可以复用,这里特别注意的是,如果不使用in.close();而仅仅使用response.close();结果就是连接会被关闭,并且不能被复用,这样就失去了采用连接池的意义。
连接池释放连接的时候,并不会直接对TCP连接的状态有任何改变,只是维护了两个Set,leased和avaliabled,leased代表被占用的连接集合,avaliabled代表可用的连接的集合,释放连接的时候仅仅是将连接从leased中remove掉了,并把连接放到avaliabled集合中
2、获取接口定义【自定义RequestExecutor】
作用:生成http请求执行
实现类中有变量:RequestHttp<H, P> requestHttp;
再具体执行过程中,通过requestHttp获取httpClient后执行execute();
构建协议:HttpGet httpGet = new HttpGet(uri);
执行请求:CloseableHttpResponse response = requestHttp.getRequestHttpClient().execute(httpGet)
public interface RequestExecutor<T, E> {
/**
* 执行http请求.
*
* @param uri uri
* @param data 数据
* @return 响应结果
* @throws WxErrorException 自定义异常
* @throws IOException io异常
*/
T execute(String uri, E data) throws WxErrorException, IOException;
/**
* 执行http请求.
*
* @param uri uri
* @param data 数据
* @param handler http响应处理器
* @throws WxErrorException 自定义异常
* @throws IOException io异常
*/
void execute(String uri, E data, ResponseHandler<T> handler) throws WxErrorException, IOException;
}