概述
http
http客户端调用apachehttp工具包,基于httpclient4.3(org.apache.http.impl.client)开发,针对http请求相关业务需求进行封装。
在网络协议中,http协议属于应用层协议,主要聚焦在规范数据层面,定义报文规则,使接收方能正确解析和响应,而在应用层下面的传输层,主要聚焦数据传输层面,包括tcp、ftp协议。http请求(连接)最终必须绑定到一个tcp连接进行上下行传输。传统http请求流程,为收到http请求,封装http报文,建立tcp连接,传输数据,收到响应,断开tcp连接,请求完成,每次请求都要新建一个tcp连接(传说中的三次握手四次挥手)。
http连接池
PoolingHttpClient是传输层tcp连接。接收到http后,包装http报文,在发送时,从连接池中获取一个tcp连接进行传输,传输完毕后不关闭tcp连接,而是归还给连接池,由连接池维护tcp连接的生命周期,从而实现tcp连接的复用,减少系统和服务端频繁建立tcp连接的消耗,在高并发时这个损耗比较大(频繁握手挥手浪费资源)。另一方面,在高并发时,通过连接池可以有效的减少并发tcp连接数,减少客户端服务监听端口消耗,监听端口数量是有限,从而提高系统吞吐量,用少量tcp连接处理大量http请求,增加处理性能。最后,对于服务端,并发的tcp连接减少了,减少了服务端处理并发峰值压力,增加服务端稳定性。
查看源码发现都在实现ConnectionSocketFactory的创建socket连接。
如下实例:
maxTotalPool
创建http连接池最大总连接数。
MAX_TIMEOUT
建立连接超时时间,创建tcp连接的超时时间。调小这个值,可以有效减少异常请求地址对连接的占用,将连接留给质量好的请求。
RequestTimeout
从连接池中获取连接的超时时间,即等待ConnectionManager释放connection的时间。在高并发情况下,连接池会不断创建新的连接,直到达到连接池最大连接数,此时连接池中已无空闲连接情况下,新的获取连接请求会处于等待状态,这个参数就是设置等待超时时间。调小这个时间,可以有效的提高响应速度并降低积压请求量,但是会增加请求失败概率。
SocketTimeout
响应超时时间,两次响应之间间隔时间,对于一般请求只有一次响应,为服务端响应超时时间,对处理时间较长的接口,需要调整此值。调小此连接也可以提高响应速度,增加系统吞吐,会增加异常请求概率,如有需要,需使用重发机制,此时服务端要考虑幂等处理。
如下为httppoolutil工具类:
package com.using.judge.web.client.utils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.springframework.stereotype.Component;
@Component
public class HttpPoolUtil {
private static Log logger = LogFactory.getLog(HttpPoolUtil.class);
public static final String UTF8 = "UTF-8";
//默认打开的
public static volatile boolean isClosed = false;
//连接池大小
public static final int maxTotalPool = 30;
//最大超时时间
public static final int MAX_TIMEOUT = 5000;
//连接池获取实例时间
public static final int RequestTimeout = 2000;
private static RequestConfig requestConfig;
private static HttpClientBuilder httpClientBuilder;
private static PoolingHttpClientConnectionManager poolConnManager;
static {
// 设置连接池
poolConnManager = new PoolingHttpClientConnectionManager();
poolConnManager.setMaxTotal(maxTotalPool);//设置连接池大小
poolConnManager.setDefaultMaxPerRoute(maxTotalPool);
RequestConfig.Builder configBuilder = RequestConfig.custom();
// 设置连接超时
configBuilder.setConnectTimeout(MAX_TIMEOUT);
// 设置读取超时
configBuilder.setSocketTimeout(MAX_TIMEOUT);
// 设置从连接池获取连接实例的超时
configBuilder.setConnectionRequestTimeout(RequestTimeout);
// 在提交请求之前 测试连接是否可用
//configBuilder.setStaleConnectionCheckEnabled(true);
requestConfig = configBuilder.build();
//
httpClientBuilder = HttpClients.custom().setConnectionManager(poolConnManager).setDefaultRequestConfig(requestConfig);
logger.info(">>>>>>>>>>> PoolingHttpClientConnectionManager初始化成功 >>>>>>>>>>>");
}/**
* 从http连接池里获取客户端实例
* @return httpClient
*/
public static CloseableHttpClient getHttpClient() {
CloseableHttpClient httpClient = httpClientBuilder.build();
if( null == httpClient ){
logger.info("---------HttpClients.createDefault()---------");
httpClient = HttpClients.createDefault();
}
return httpClient;
}/**
* 关闭连接池资源
*/
public synchronized static void closePool() {
if( !isClosed ){
isClosed = true;
poolConnManager.close();
}
}
}
消费http连接池的实例
public File returnFileHttpPool(String urlStr, String fileName, String savePath) throws IOException {
//拿到连接池
CloseableHttpClient httpClient = HttpPoolUtil.getClient();
CloseableHttpResponse response=null;
try {
//执行get请求
response = httpClient.execute(new HttpGet(urlStr));
if(response.getStatusLine().getStatusCode() != HttpStatus.SC_OK) {
log.debug("http 请求错误,错误码:"+response.getStatusLine().getStatusCode());
return null;
}
//获取响应消息内容
InputStream inputStream = response.getEntity().getContent();
// 获取自己数组
byte[] getData = readInputStream(inputStream);
// 文件保存位置
File saveDir = new File(savePath);
if (!saveDir.exists()) {
saveDir.mkdir();
}
File file = new File(saveDir + File.separator + fileName);
FileOutputStream fos = new FileOutputStream(file);
fos.write(getData);
if (fos != null) {
fos.close();
}
if (inputStream != null) {
inputStream.close();
}
return file;
} catch (Exception e) {
log.info("调用"+urlStr+"出错:"+e.getMessage());
}
return null;
}
消费http实例:
//获取httpclient
HttpClient client = new DefaultHttpClient();
详细阅读了关于tcp/ip协议,参考链接。