HttpClient连接池小记
**欢迎关注公众号**
**微信扫一扫**
【仅需9.9订阅专栏合集,作者所有付费文章都能看(持续更新)】
推荐【Kafka教程】https://bigbird.blog.csdn.net/article/details/108770504
推荐【rabbitmq教程】https://bigbird.blog.csdn.net/article/details/81436980
推荐【Flink教程】https://blog.csdn.net/hellozpc/article/details/109413465
推荐【JVM面试与调优教程】https://bigbird.blog.csdn.net/article/details/113888604
推荐【SpringBoot全套教程】https://blog.csdn.net/hellozpc/article/details/107095951
推荐【SpringCloud教程】https://blog.csdn.net/hellozpc/article/details/83692496
推荐【Mybatis教程】https://blog.csdn.net/hellozpc/article/details/80878563
推荐【SnowFlake教程】https://blog.csdn.net/hellozpc/article/details/108248227
推荐【并发限流教程】https://blog.csdn.net/hellozpc/article/details/107582771
推荐【Redis教程】https://bigbird.blog.csdn.net/article/details/81267030
推荐【Netty教程】https://blog.csdn.net/hellozpc/category_10945233.html
推荐Springboot集成http连接池
https://blog.csdn.net/hellozpc/article/details/106861972
- 常用的http客户端有JDK原生的URLConnection、Netty的异步HTTP Client、Spring的RestTemplate、Spring Cloud中的Feign。
虽然RestTemplate、Feign使用极其方便,但是屏蔽了太多底层细节,不利于全局把控。 本文主要记载一下基于Apache HttpClient 的http 连接池处理。网上很多文章都没有关注定期检测关闭无效连接这块功能。另外要注册jvm钩子在程序退出时关闭httpClient实例。
/**
- 业务层调用HttpTemplate#execute发送http post请求
@author zhoupengcheng
*/
public class HttpTemplate {private static Logger logger = LoggerFactory.getLogger(HttpTemplate.class);
private static final String DEFAULT_ACCEPTTYPE = “application/json;charset=utf-8”;
private static final String DEFAULT_CHARSET = “utf-8”;/**
最大连接数
*/
private static final int MAX_CONNECTION_NUM = 20;/**
单路由最大连接数
*/
private static final int MAX_PER_ROUTE = 20;/**
连接超时时间,缺省为3秒钟
*/
private static final int DEFAULT_CONNECTION_TIMEOUT = 3000;/**
读取超时时间,缺省为3秒钟
*/
private static final int DEFAULT_READ_TIMEOUT = 3000;/**
连接池管理对象
*/
private PoolingHttpClientConnectionManager cm;private CloseableHttpClient httpClient;
/**
反序列化工具
*/
private ObjectMapper objectMapper;public HttpTemplate() {
init();
}public void init() {
initObjectMapper();
initHttpClient();
}private void initHttpClient() {
HttpClientBuilder builder = HttpClients.custom();
builder.disableCookieManagement();
builder.disableRedirectHandling();
builder.disableAutomaticRetries();builder.setConnectionManagerShared(false)
.evictIdleConnections(1, TimeUnit.MINUTES)// 定期回收空闲连接
.evictExpiredConnections()// 定期回收过期连接
.setConnectionTimeToLive(1, TimeUnit.MINUTES) // 连接存活时间,如果不设置,则根据长连接信息决定
.setDefaultRequestConfig(
RequestConfig
.custom()
.setAuthenticationEnabled(false)
.setCircularRedirectsAllowed(false)
.setSocketTimeout(DEFAULT_READ_TIMEOUT)
.setConnectTimeout(DEFAULT_CONNECTION_TIMEOUT)
.setConnectionRequestTimeout(1000)
.setCookieSpec(CookieSpecs.IGNORE_COOKIES)
.build()) // 设置默认请求配置
.setConnectionReuseStrategy(DefaultConnectionReuseStrategy.INSTANCE) // 连接重用策略 是否能keepAlive
.setKeepAliveStrategy(DefaultConnectionKeepAliveStrategy.INSTANCE)// 长连接配置,即获取长连接生产多长时间
.setRetryHandler(new DefaultHttpRequestRetryHandler(0, false))// 重试次数 默认3次 此处禁用
.build();
Registry
socketFactoryRegistry = RegistryBuilder.<ConnectionSocketFactory>create()
.register("https", PlainConnectionSocketFactory.getSocketFactory())
.register("http", new PlainConnectionSocketFactory()).build();
final int conExpire = 15;// 长连接闲置过期时间,可配置
cm = new PoolingHttpClientConnectionManager(socketFactoryRegistry, null, null, null, conExpire,TimeUnit.SECONDS);
cm.setDefaultConnectionConfig(ConnectionConfig.DEFAULT);
cm.setMaxTotal(MAX_CONNECTION_NUM);
cm.setDefaultMaxPerRoute(MAX_PER_ROUTE);
// 设置长连接心跳检测,设置超时,禁用nagle算法
cm.setDefaultSocketConfig(SocketConfig.custom().setSoKeepAlive(true).setSoTimeout(DEFAULT_READ_TIMEOUT).setTcpNoDelay(true).build());
cm.setValidateAfterInactivity(-1);// 每次取重用的连接时禁止连接活性检测,可以提升性能builder.setConnectionManager(cm);
builder.setConnectionManagerShared(false);httpClient = builder.build();
// 过期检测
Thread staleCheckThread = new Thread(() -> {while (true) {
try {
Thread.sleep(10000);
cm.closeExpiredConnections();
cm.closeIdleConnections(conExpire, TimeUnit.SECONDS);
} catch (Exception e) {
logger.error("stale check exception", e);
}
}
}, “HttpInvoker-coonection-stale-check-thread”);
// 设置成为守护线程
staleCheckThread.setDaemon(true);
staleCheckThread.start();// 程序退出时,关闭httpClient
Runtime.getRuntime().addShutdownHook(new Thread(() -> {try {
httpClient.close();
logger.info(">>>>httpClient closed<<<<");
} catch (Exception e) {
logger.warn("httpClient close exception", e);
}
}, “srsynchronize-httpInvoker-shutdown-thread”));
}private void initObjectMapper() {
objectMapper = new ObjectMapper();
objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
objectMapper.configure(JsonParser.Feature.ALLOW_SINGLE_QUOTES, true);
objectMapper.configure(JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES, true);
}/**
- 暴露给业务层调用的方法
- @param path 请求url
- @param req 请求对象
- @param typeReference 响应对象
- @param
- @return
@throws Exception
*/
publicR execute(String path, Object req, TypeReference typeReference) throws Exception {
if (typeReference == null) {typeReference = (TypeReference<R>) new TypeReference<Response>() {
};
}
String reqBody = objectMapper.writeValueAsString(req);
logger.info(“reqBody:{}”, reqBody);HttpPost httpPost = new HttpPost(path);
httpPost.setProtocolVersion(HttpVersion.HTTP_1_1);
httpPost.setHeader(HTTP.CONN_DIRECTIVE, HTTP.CONN_KEEP_ALIVE);
httpPost.setConfig(RequestConfig.custom().setSocketTimeout(DEFAULT_READ_TIMEOUT).setConnectTimeout(DEFAULT_CONNECTION_TIMEOUT).build());
httpPost.addHeader(“Accept”, DEFAULT_ACCEPTTYPE);
httpPost.addHeader(“Content-Type”, DEFAULT_ACCEPTTYPE);HttpEntity httpEntity = new StringEntity(reqBody, DEFAULT_CHARSET);
httpPost.setEntity(httpEntity);CloseableHttpResponse httpResponse = httpClient.execute(httpPost);
String responseBody = EntityUtils.toString(httpResponse.getEntity(), DEFAULT_CHARSET);
logger.info(“resBody:{}”, responseBody);
return objectMapper.readValue(responseBody, typeReference);
}/**
- demo
* - @param args
@throws Exception
*/
public static void main(String[] args) throws Exception {
HttpTemplate httpTemplate = new HttpTemplate();
Order order = new Order();
order.id = “1111111”;
order.cancleRemarks = “擦擦擦”;
order.createDate = LocalDateTime.now();
order.startLongitude = “1356.4343”;
OrderResponse orderResponse = httpTemplate.execute(“http://127.0.0.1:8888/ordersdispatch“, order, new TypeReference() {
});//不传具体的OrderResponse会使用默认的Response
Response response = httpTemplate.execute(“http://127.0.0.1:8888/orderdispatch“, order, null);logger.info(“orderResponse:{}”, orderResponse);
logger.info(“response:{}”, response);
}
}
public class Order {
public String id;
public String orderCode;
public String startCityName;
public String startAddress;
public String startAddDtl;
public String startLongitude;
public String startLatitude;
public String endCityName;
public String endAddress;
public String endAddDtl;
public String endLongitude;
public String endLatitude;
public byte prodType;
public LocalDateTime orderDate;
public int orderDay;
public String flightNo;
public String remark;
public int orderState;
public String driverId;
public String driverUid;
public String driverMobile;
public String source;
public LocalDateTime createDate;
public LocalDateTime updateDate;
public String dispatchType;
public String driverRemark;
public LocalDateTime flightDate;
public String flightArrCode;
public String flightDepCode;
public String cancleRemarks;
public LocalDateTime endTime;
}
/**
- http响应实体
*/
public class Response {
public static int SUCCESS_CODE = 0;
public Integer retCode;
public String retMsg;
public static boolean isSuccess(Response res) {
}return res != null && res.retCode == SUCCESS_CODE;
@Override
public String toString() {
}return "Response{" +
"retCode=" + retCode +
", retMsg='" + retMsg + '\'' +
'}';
}
public class OrderResponse extends Response {
public List<Order> info;
}
**欢迎关注公众号**
**微信扫一扫**
还没有评论,来说两句吧...