简介

  • 对于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篇的更多相关文章

  1. 打造程序员的高效生产力工具-mac篇

    打造程序员的高效生产力工具-mac篇 1   概述 古语有云:“工欲善其事,必先利其器” [1] ,作为一个程序员,他最重要的生产资源是脑力知识,最重要的生产工具是什么?电脑. 在进行重要的脑力成果输 ...

  2. [转]Android开源项目第二篇——工具库篇

    本文为那些不错的Android开源项目第二篇--开发工具库篇,主要介绍常用的开发库,包括依赖注入框架.图片缓存.网络相关.数据库ORM建模.Android公共库.Android 高版本向低版本兼容.多 ...

  3. Android开源项目第二篇——工具库篇

    本文为那些不错的Android开源项目第二篇——开发工具库篇,**主要介绍常用的开发库,包括依赖注入框架.图片缓存.网络相关.数据库ORM建模.Android公共库.Android 高版本向低版本兼容 ...

  4. 我的日常工具——gdb篇

    我的日常工具——gdb篇 03 Apr 2014 1.gdb的原理 熟悉linux的同学面试官会问你用过gdb么?那好用过,知道gdb是怎么工作的么?然后直接傻眼... gdb是怎么接管一个进程?并且 ...

  5. Linux工具参考篇(网摘)

    Linux工具参考篇 原文出处:[Linux Tools Quick Tutorial] 1. gdb 调试利器 2. ldd 查看程序依赖库 3. lsof 一切皆文件 4. ps 进程查看器 5. ...

  6. 第一篇:打造专属开发工具Eclipse篇

    第一篇:打造专属开发工具Eclipse篇 eclipse 优化 1.动画很酷,但如果可以的话,我总是在所有的工具中禁用动画.所以classic或者window classic主题是我最常用的主题 , ...

  7. Java开源网页抓取工具httpClient以及jsoup

    网上看到不错的Java网页抓取工具和库 先记录一下 使用java开源工具httpClient及jsoup抓取解析网页数据

  8. <JVM下篇:性能监控与调优篇>03-JVM监控及诊断工具-GUI篇

    笔记来源:尚硅谷JVM全套教程,百万播放,全网巅峰(宋红康详解java虚拟机) 同步更新:https://gitee.com/vectorx/NOTE_JVM https://codechina.cs ...

  9. javaSE中级篇2 — 工具类篇 — 更新完毕

    1.工具类(也叫常用类)-- 指的是别人已经写好了的,我们只需要拿来用就行了 官网网址:Overview (Java Platform SE 8 ) (oracle.com) ---- 但是这个是英文 ...

随机推荐

  1. 版本控制工具 GIT入门教程

    GIT 在团队中的中作流程 1.每个程序员在自己的分支上进行开发 2.主程序猿/Leader合并程序员程序 3.程序员之间也可以对一下提交冲突进行合并 下载和安装 GIT官方网址:http:// gi ...

  2. java 中的自动装箱和拆箱操作

    在前面的文章中提到,Java为每种基本数据类型都提供了对应的包装器类型,至于为什么会为每种基本数据类型提供包装器类型在此不进行阐述,有兴趣的朋友可以查阅相关资料.在Java SE5之前,如果要生成一个 ...

  3. python黑帽子之tcp服务端

    试着用python创建一个标准的多线程tcp服务器 import socket import threading bind_ip = "0.0.0.0" bind_port = 8 ...

  4. Javascript中的"函数是第一类对象(first-class object)"

    本身这句话很好解释,函数有两个主要特点,援引自 陈新 译的<JavaScript模式>: 1.函数是第一类对象: 函数可以在运行时动态创建,还可以在程序执行过程中创建. 函数可以分配变量, ...

  5. 02 . 分布式存储之FastDFS 高可用集群部署

    单节点部署和原理请看上一篇文章 https://www.cnblogs.com/you-men/p/12863555.html 环境 [Fastdfs-Server] 系统 = CentOS7.3 软 ...

  6. Drools之基础语法

    一.规则文件 标准的规则文件以”.drl”结尾. 一套完整的规则文件内容如下: package:包名,只限于逻辑上的管理,若自定义的查询或函数位于同一包名,不管物理位置如何,都可以直接调用. impo ...

  7. Appium移动端自动化测试--搭建模拟器和真机测试环境

    详细介绍安装Android Studio及Android SDK.安装Appium Server. 文章目录如下 目录 文章目录如下 模拟器--安装Android Studio及Android SDK ...

  8. 在具体的前端工作中通常HTML页面乱码怎么解决?

    HTML文件乱码一般是因为编码格式不匹配造成的,比如:不同编码内容混杂.浏览器不能自动检测网页编码等等:但无论是哪种情况造成乱码,在HTML文件头中设置网页编码,匹配好编码格式就可. 下面是一个中文乱 ...

  9. Python Java 快速配置环境变量(Path)

    Python Java 快速配置环境变量(Path) 最近系统被重置,清空了C盘中的program等文件夹以及初始化了环境变量. 通常环境下,在windows环境中我们都会打开"环境变量&q ...

  10. P2585 三色二叉树 题解

    题目 一棵二叉树可以按照如下规则表示成一个由0.1.2组成的字符序列,我们称之为"二叉树序列S": \[S=\left\{ \begin{aligned} 0 &\ \ 表 ...