【工具】- HttpClient篇
简介
- 对于
httpclient
,相信很多人或多或少接触过,对于httpclient
的使用姿势,相信很多人会有疑问?下面这边会通过代码说明
package xxx;
import org.apache.commons.codec.Charsets;
import org.apache.http.HeaderElement;
import org.apache.http.HeaderElementIterator;
import org.apache.http.HttpEntity;
import org.apache.http.NameValuePair;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.protocol.HttpClientContext;
import org.apache.http.conn.ConnectionKeepAliveStrategy;
import org.apache.http.conn.HttpClientConnectionManager;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.message.BasicHeaderElementIterator;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.protocol.HTTP;
import org.apache.http.protocol.HttpContext;
import org.apache.http.util.EntityUtils;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
public class HttpClientUtil implements AutoCloseable {
private volatile static HttpClientUtil httpClientUtil;
private final CloseableHttpClient httpClient;
private final PoolingHttpClientConnectionManager connMgr;
private final ThreadLocal<HttpClientContext> httpContext = ThreadLocal.withInitial(HttpClientContext::create);
private final IdleConnectionEvictThread evictThread;
public HttpClientUtil() {
// 自定义keep-alive策略,keep-alive使得tcp连接可以被复用。
// 但默认的keep-alive时长为无穷大,不符合现实,所以需要改写,定义一个更短的时间
// 如果服务器http响应头包含 "Keep-Alive:timeout=" ,则使用timeout=后面指定的值作为keep-alive的时长,否则默认60秒
ConnectionKeepAliveStrategy strategy = (httpResponse, httpContext) -> {
HeaderElementIterator it = new BasicHeaderElementIterator(httpResponse.headerIterator(HTTP.CONN_KEEP_ALIVE));
while (it.hasNext()) {
HeaderElement he = it.nextElement();
String param = he.getName();
String value = he.getValue();
if (value != null && param.equalsIgnoreCase("timeout")) {
return Long.parseLong(value) * 1000;
}
}
return 60 * 1000;
};
// 自定义连接池,连接池可以连接可以使连接可以被复用。
// 连接的复用需要满足:Keep-alive + 连接池。keep-alive使得连接能够保持存活,不被系统销毁;连接池使得连接可以被程序重复引用
// 默认的连接池,DefaultMaxPerRoute只有5个,MaxTotal只有10个,不满足实际的生产需求
connMgr = new PoolingHttpClientConnectionManager();
// 最大连接数500
connMgr.setMaxTotal(500);
// 同一个ip:port的请求,最大连接数200
connMgr.setDefaultMaxPerRoute(200);
// 启动线程池空闲连接、超时连接监控线程
evictThread = new IdleConnectionEvictThread(connMgr);
evictThread.start();
// 定义请求超时配置
RequestConfig requestConfig = RequestConfig.custom()
.setConnectionRequestTimeout(5000) // 从连接池里获取连接的超时时间
.setConnectTimeout(2000) // 建立TCP的超时时间
.setSocketTimeout(10000) // 读取数据包的超时时间
.build();
httpClient = HttpClients.custom()
.setConnectionManager(connMgr)
.setKeepAliveStrategy(strategy)
.setDefaultRequestConfig(requestConfig)
.build();
}
/**
* 因为HttpClient是线程安全的,可以提供给多个线程复用,同时连接池的存证的目的就是为了可以复用连接,所以提供单例模式
*/
private static class HttpClientUtilHolder {
private static final HttpClientUtil INSTANCE = new HttpClientUtil();
}
public static HttpClientUtil getInstance() {
return HttpClientUtilHolder.INSTANCE;
}
public PoolingHttpClientConnectionManager getConnMgr() {
return connMgr;
}
public HttpContext getHttpContext() {
return httpContext.get();
}
public CloseableHttpClient getHttpClient() {
return httpClient;
}
/**
* http get操作
* @param url 请求地址
* @param headers 请求头
* @return 返回响应内容
*/
public String get(String url, Map<String, String> headers) {
HttpGet httpGet = new HttpGet(url);
if (headers != null) {
for (Map.Entry<String, String> header : headers.entrySet()) {
httpGet.setHeader(header.getKey(), header.getValue());
}
}
CloseableHttpResponse response = null;
try {
response = httpClient.execute(httpGet, httpContext.get());
HttpEntity entity = response.getEntity();
return EntityUtils.toString(entity, Charsets.UTF_8);
} catch (Exception ex) {
ex.printStackTrace();
} finally {
if (response != null) {
try {
//上面的EntityUtils.toString会调用inputStream.close(),进而也会触发连接释放回连接池,但因为httpClient.execute可能抛异常,所以得在finally显示调一次,确保连接一定被释放
response.close();
} catch (IOException ex) {
ex.printStackTrace();
}
}
}
return null;
}
/**
* form表单post
*
* @param url 请求地址
* @param params 参数内容
* @param headers http头信息
* @return http文本格式响应内容
*/
public String postWithForm(String url, Map<String, String> params, Map<String, String> headers) {
HttpPost httpPost = new HttpPost(url);
if (headers != null) {
for (Map.Entry<String, String> header : headers.entrySet()) {
httpPost.setHeader(header.getKey(), header.getValue());
}
}
List<NameValuePair> formParams = new ArrayList<>();
if (params != null) {
for (Map.Entry<String, String> param : params.entrySet()) {
formParams.add(new BasicNameValuePair(param.getKey(), param.getValue()));
}
}
UrlEncodedFormEntity formEntity = new UrlEncodedFormEntity(formParams, Charsets.UTF_8);
httpPost.setEntity(formEntity);
CloseableHttpResponse response = null;
try {
response = httpClient.execute(httpPost, httpContext.get());
HttpEntity entity = response.getEntity();
return EntityUtils.toString(entity, Charsets.UTF_8);
} catch (Exception ex) {
ex.printStackTrace();
} finally {
if (response != null) {
try {
response.close();
} catch (IOException ex) {
ex.printStackTrace();
}
}
}
return null;
}
/**
* json内容post
*
* @param url 请求地址
* @param data json报文
* @param headers http头
* @return http文本格式响应内容
*/
public String postWithBody(String url, String data, Map<String, String> headers) {
HttpPost httpPost = new HttpPost(url);
if (headers != null) {
for (Map.Entry<String, String> header : headers.entrySet()) {
httpPost.setHeader(header.getKey(), header.getValue());
}
}
StringEntity stringEntity = new StringEntity(data, ContentType.create("plain/text", Charsets.UTF_8));
httpPost.setEntity(stringEntity);
httpPost.setHeader("Content-type", "application/json");
CloseableHttpResponse response = null;
try {
response = httpClient.execute(httpPost, httpContext.get());
HttpEntity entity = response.getEntity();
return EntityUtils.toString(entity, Charsets.UTF_8);
} catch (Exception ex) {
ex.printStackTrace();
} finally {
if (response != null) {
try {
response.close();
} catch (IOException ex) {
ex.printStackTrace();
}
}
}
return null;
}
/**
* HttpClientUtil类回收时,通过httpClient.close(),可以间接的关闭连接池,从而关闭连接池持有的所有tcp连接
* 与response.close()的区别:response.close()只是把某个请求持有的tcp连接放回连接池,而httpClient().close()是销毁整个连接池
*
* @throws IOException
*/
@Override
public void close() throws IOException {
httpClient.close();
evictThread.shutdown();
}
/**
* 定义一个连接监控线程类,用以从连接池里关闭过期的连接(即服务器已经关闭的连接),以及在30秒内处于空闲的连接;每5秒钟处理一次
*/
static class IdleConnectionEvictThread extends Thread {
private final HttpClientConnectionManager connMgr;
private volatile boolean shutdown;
public IdleConnectionEvictThread(HttpClientConnectionManager connMgr) {
super();
this.connMgr = connMgr;
setDaemon(true);
}
@Override
public void run() {
try {
while (!shutdown) {
synchronized (this) {
wait(5000);
connMgr.closeExpiredConnections();
connMgr.closeIdleConnections(30, TimeUnit.SECONDS);
}
}
} catch (InterruptedException ex) {
ex.printStackTrace();
}
}
/**
* 关闭监控线程
*/
public void shutdown() {
shutdown = true;
// 监控线程可能还处于wait()状态,通过notifyAll()唤醒,及时退出while循环
synchronized (this) {
notifyAll();
}
}
}
}
参考书籍:httpclient-tutorial.pdf
【工具】- HttpClient篇的更多相关文章
- 打造程序员的高效生产力工具-mac篇
打造程序员的高效生产力工具-mac篇 1 概述 古语有云:“工欲善其事,必先利其器” [1] ,作为一个程序员,他最重要的生产资源是脑力知识,最重要的生产工具是什么?电脑. 在进行重要的脑力成果输 ...
- [转]Android开源项目第二篇——工具库篇
本文为那些不错的Android开源项目第二篇--开发工具库篇,主要介绍常用的开发库,包括依赖注入框架.图片缓存.网络相关.数据库ORM建模.Android公共库.Android 高版本向低版本兼容.多 ...
- Android开源项目第二篇——工具库篇
本文为那些不错的Android开源项目第二篇——开发工具库篇,**主要介绍常用的开发库,包括依赖注入框架.图片缓存.网络相关.数据库ORM建模.Android公共库.Android 高版本向低版本兼容 ...
- 我的日常工具——gdb篇
我的日常工具——gdb篇 03 Apr 2014 1.gdb的原理 熟悉linux的同学面试官会问你用过gdb么?那好用过,知道gdb是怎么工作的么?然后直接傻眼... gdb是怎么接管一个进程?并且 ...
- Linux工具参考篇(网摘)
Linux工具参考篇 原文出处:[Linux Tools Quick Tutorial] 1. gdb 调试利器 2. ldd 查看程序依赖库 3. lsof 一切皆文件 4. ps 进程查看器 5. ...
- 第一篇:打造专属开发工具Eclipse篇
第一篇:打造专属开发工具Eclipse篇 eclipse 优化 1.动画很酷,但如果可以的话,我总是在所有的工具中禁用动画.所以classic或者window classic主题是我最常用的主题 , ...
- Java开源网页抓取工具httpClient以及jsoup
网上看到不错的Java网页抓取工具和库 先记录一下 使用java开源工具httpClient及jsoup抓取解析网页数据
- <JVM下篇:性能监控与调优篇>03-JVM监控及诊断工具-GUI篇
笔记来源:尚硅谷JVM全套教程,百万播放,全网巅峰(宋红康详解java虚拟机) 同步更新:https://gitee.com/vectorx/NOTE_JVM https://codechina.cs ...
- javaSE中级篇2 — 工具类篇 — 更新完毕
1.工具类(也叫常用类)-- 指的是别人已经写好了的,我们只需要拿来用就行了 官网网址:Overview (Java Platform SE 8 ) (oracle.com) ---- 但是这个是英文 ...
随机推荐
- SpringMVC 学习笔记(六)拦截器
5.1.处理器拦截器简介 Spring Web MVC的处理器拦截器(如无特殊说明,下文所说的拦截器即处理器拦截器) 类似于Servlet开发中的过滤器Filter,用于对处理器进行预处理和后处理. ...
- python 2 与python 3区别汇总
python 2 与python 3区别汇总 一.核心类差异1. Python3 对 Unicode 字符的原生支持.Python2 中使用 ASCII 码作为默认编码方式导致 string 有两种类 ...
- Idea配置JRebel插件的详细配置及图解
Idea最新JRebel插件的详细配置及图解 地址:https://blog.csdn.net/nyotengu/article/details/80629631#commentBox Ⅰ安装jreb ...
- node+ajax实战案例(3)
3.用户注册实现 3.1.注册用户功能的实现逻辑 1 用户在表单上输入注册信息 2 点击注册后,收集用户在表单上输入的注册信息并且发送给后台 3 后台接收用户发送过来的注册信息 4 后台需要处理数据并 ...
- nginx 注释配置及详解
前言 DMZ(Demilitarized Zone) 非军事区,生产环境 WEB 服务部署的区域,公司的架构为一台nginx 充当 load balance 服务,负载到两台 nginx 上面,反向代 ...
- 【错误】fatal: destination path already exists and is not an empty directory. 错误及解决办法
今天在使用Git for Windows clone代码时,遇到了题目所示的错误,简单来说就是目标路径‘.’已经存在并且不是一个空目录. 可是在我在文件夹下并没有看到任何文件,显示“该文件夹为空”,然 ...
- jvm字节码和类加载机制
Class类文件的结构 任何一个Class文件都对应着唯一一个类或接口的定义信息,但反过来说,类或接口并不一定都得定义在文件里(类和接口也可以用反射的方式通过类加载器直接生成) Class文件时一组以 ...
- 使用Xmanager连接linux,操作“xhost +”时出现类似“xhost: unable to open display "192.168.1.1811:1.0" ”问题的解决
远程连接linux服务器时,有的时候需要把服务器上的图形界面投影到本地来进一步操作,比如linux下安装oracle时就需要在oracle用户下允许视图状态投影到本地,这需要使用命令: xhost + ...
- NLP(一)
“自然语言处理”(Natural Language Processing 简称 NLP)包含所有用计算机对自然语言进行的操作. 自然语言工具包(NLTK) 语言处理任务与相应 NLTK 模块以及功能描 ...
- LeetCode-Stack-Easy
简单题 1. 有效的括号(leetcode-20) 给定一个只包括 '(',')','{','}','[',']' 的字符串,判断字符串是否有效. 有效字符串需满足: 1. 左括号必须用相同类型的右括 ...