【转】同步的HttpClient使用详解
http://blog.csdn.net/angjunqiang/article/details/54340398
背景
服务端以及客户端在开发过程中不可避免的会使用到网络请求,网络请求可以使用Java原生的URLConnection,也可以使用HttpClient。在日常工作中建议大家使用HttpClient。URLConnection需要自己去实现相关的方法,而HttpClient使用起来方便,且提供许多的API。
HttpClient
1、httpClient的特性
实现了所有 HTTP 的方法(GET,POST,PUT,HEAD 等)
支持自动转向
支持 HTTPS 协议
支持代理服务器等
2、httpClient版本
目前httpClient的最新版本为4.5.2,可以通过如下maven获取Jar包,这里以本人常用的4.2.2为例;
- <span style="font-size:14px;"> <!-- https://mvnrepository.com/artifact/org.apache.httpcomponents/httpclient -->
- <dependency>
- <groupId>org.apache.httpcomponents</groupId>
- <artifactId>httpclient</artifactId>
- <version>4.2.2</version>
- </dependency></span>
3、使用方法
- package com.netease.mail.be.appads.web.controller;
- import java.io.IOException;
- import java.io.UnsupportedEncodingException;
- import java.net.URI;
- import java.net.URISyntaxException;
- import java.util.List;
- import org.apache.commons.lang.StringUtils;
- import org.apache.http.HttpEntity;
- import org.apache.http.HttpResponse;
- import org.apache.http.client.ClientProtocolException;
- import org.apache.http.client.HttpClient;
- import org.apache.http.client.entity.UrlEncodedFormEntity;
- import org.apache.http.client.methods.HttpPost;
- import org.apache.http.entity.StringEntity;
- import org.apache.http.impl.client.DefaultHttpClient;
- import org.apache.http.message.BasicNameValuePair;
- import org.apache.http.protocol.HTTP;
- import org.apache.http.util.EntityUtils;
- public class PostSample {
- public String setPost(String url, String reqBody,
- List<BasicNameValuePair> urlParams) {
- // step1: 构造HttpClient的实例,类似于打开浏览器
- HttpClient client = new DefaultHttpClient();
- // step2: 创建POST方法的实例,类似于在浏览器地址栏输入url
- HttpPost postMethod = new HttpPost(url);
- String returnValue = "";
- try {
- // step3:设置url相关参数
- if (null != urlParams && !urlParams.isEmpty()) {
- String params = EntityUtils.toString(new UrlEncodedFormEntity(
- urlParams, HTTP.UTF_8));
- postMethod.setURI(new URI(postMethod.getURI().toString() + "?"
- + params));
- }
- // step4:设置请求body参数及头部相关参数
- if (StringUtils.isNotBlank(reqBody)) {
- StringEntity se = new StringEntity(reqBody, "UTF-8");
- se.setContentType("application/text; charset=utf-8");
- postMethod.setEntity(se);
- }
- // step5:执行发送请求
- HttpResponse response = client.execute(postMethod);
- int statusCode = response.getStatusLine().getStatusCode();
- if (statusCode == 200) {
- // 校验码
- HttpEntity he = response.getEntity();
- returnValue = new String(EntityUtils.toByteArray(he), "UTF-8");
- System.out.println("Bid Request Send Succeed." + returnValue);
- return returnValue;
- } else if (statusCode == 204) {
- System.out.println("Bid Request Send Error, No Content");
- } else {
- System.out.println("Error happens,http status code is "
- + statusCode + ".");
- }
- } catch (UnsupportedEncodingException e) {
- System.out.println(Thread.currentThread().getName()
- + "发送BidRequest请求错误,编码错误:" + e.getMessage());
- } catch (ClientProtocolException e) {
- System.out.println(Thread.currentThread().getName()
- + "发送BidRequest请求错误,协议错误错误:" + e.getMessage());
- } catch (IOException e) {
- System.out.println(Thread.currentThread().getName()
- + "发送BidRequest请求错误,网络IO错误:" + e.getMessage());
- } catch (URISyntaxException e) {
- System.out.println(Thread.currentThread().getName()
- + "发送BidRequest请求错误,URL语法错误:" + e.getMessage());
- } finally {
- // 释放连接,很关键
- postMethod.releaseConnection();
- }
- return returnValue;
- }
- }
注:此方法可以应付并发量不高的网络请求,但是对于高并发的网络请求,此方法存在很大的缺陷及性能问题。具体原因如下:
- 此方法每次都会new一个httpClient对象及HttpPost对象,new的时候需要申请系统资源会消耗一定的时间;
- 虽然请求执行完毕后都执行了postMethod.releaseConnection方法去释放连接,但是这只是应用层的释放,而真正的释放端口需等待一段时间, 这个时间由系统决定。所以在高并发的时候,端口不断的被占用达到系统的最大值,导致后面的网络请求会直接抛出 Cannot assign requested address[不能分配端口]的异常],从而不能进行请求。
高并发的网络请求
在高并发时,需要用到HttpClient的连接池,连接的作用主要是减少创建链接的次数,请求可以复用之前的连接,所以就可以指定一定数量的连接数让连接池去创建,避免了多次创建新的连接并在系统回收连接时不能及时释放端口的情况下达到可用端口的最大值的问题。具体实现如下:
- package com.netease.mail.be.appads.web.controller;
- import java.io.IOException;
- import java.io.UnsupportedEncodingException;
- import java.net.URI;
- import java.net.URISyntaxException;
- import java.nio.charset.Charset;
- import java.security.cert.CertificateException;
- import java.security.cert.X509Certificate;
- import java.util.List;
- import javax.net.ssl.SSLContext;
- import javax.net.ssl.TrustManager;
- import javax.net.ssl.X509TrustManager;
- import org.apache.commons.lang.StringUtils;
- import org.apache.http.HttpEntity;
- import org.apache.http.HttpResponse;
- import org.apache.http.HttpVersion;
- import org.apache.http.client.ClientProtocolException;
- import org.apache.http.client.entity.UrlEncodedFormEntity;
- import org.apache.http.client.methods.HttpGet;
- import org.apache.http.client.methods.HttpPost;
- import org.apache.http.conn.scheme.PlainSocketFactory;
- import org.apache.http.conn.scheme.Scheme;
- import org.apache.http.conn.scheme.SchemeRegistry;
- import org.apache.http.conn.ssl.SSLSocketFactory;
- import org.apache.http.entity.StringEntity;
- import org.apache.http.impl.client.DefaultHttpClient;
- import org.apache.http.impl.conn.PoolingClientConnectionManager;
- import org.apache.http.message.BasicNameValuePair;
- import org.apache.http.params.BasicHttpParams;
- import org.apache.http.params.CoreConnectionPNames;
- import org.apache.http.params.CoreProtocolPNames;
- import org.apache.http.params.HttpParams;
- import org.apache.http.util.EntityUtils;
- import org.slf4j.Logger;
- import org.slf4j.LoggerFactory;
- import org.springframework.stereotype.Service;
- /**
- * HTTP请求对象,支持post与get方法
- */
- @Service("httpRequesterService")
- public class HttpClientPoolSample {
- private Logger logger = LoggerFactory.getLogger(HttpClientPoolSample.class);
- private static int socketTimeout = 1000;// 设置等待数据超时时间5秒钟 根据业务调整
- private static int connectTimeout = 2000;// 连接超时
- private static int maxConnNum = 4000;// 连接池最大连接数
- private static int maxPerRoute = 1500;// 每个主机的并发最多只有1500
- private static PoolingClientConnectionManager cm;
- private static HttpParams httpParams;
- private static final String DEFAULT_ENCODING = Charset.defaultCharset()
- .name();
- static {
- SchemeRegistry sr = new SchemeRegistry();
- sr.register(new Scheme("http", 80, PlainSocketFactory
- .getSocketFactory()));
- SSLSocketFactory sslFactory;
- try {
- SSLContext sslContext = SSLContext.getInstance("SSL");
- X509TrustManager tm = new X509TrustManager() {
- @Override
- public void checkClientTrusted(X509Certificate[] chain,
- String authType) throws CertificateException {
- }
- @Override
- public void checkServerTrusted(X509Certificate[] chain,
- String authType) throws CertificateException {
- }
- @Override
- public X509Certificate[] getAcceptedIssuers() {
- return null;
- }
- };
- sslContext.init(null, new TrustManager[] { tm },
- new java.security.SecureRandom());
- sslFactory = new SSLSocketFactory(sslContext,
- SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
- sr.register(new Scheme("https", 443, sslFactory));
- } catch (Exception e) {
- e.printStackTrace();
- }
- // 初始化连接池
- cm = new PoolingClientConnectionManager(sr);
- cm.setMaxTotal(maxConnNum);
- cm.setDefaultMaxPerRoute(maxPerRoute);
- httpParams = new BasicHttpParams();
- httpParams.setParameter(CoreProtocolPNames.PROTOCOL_VERSION,
- HttpVersion.HTTP_1_1);
- httpParams.setIntParameter(CoreConnectionPNames.CONNECTION_TIMEOUT,
- connectTimeout);// 请求超时时间
- httpParams.setIntParameter(CoreConnectionPNames.SO_TIMEOUT,
- socketTimeout);// 读取数据超时时间
- // 如果启用了NoDelay策略,httpclient和站点之间传输数据时将会尽可能及时地将发送缓冲区中的数据发送出去、而不考虑网络带宽的利用率,这个策略适合对实时性要求高的场景
- httpParams.setBooleanParameter(CoreConnectionPNames.TCP_NODELAY, true);
- httpParams.setBooleanParameter(
- CoreConnectionPNames.STALE_CONNECTION_CHECK, true);
- }
- public DefaultHttpClient getHttpClient() {
- return new DefaultHttpClient(cm, httpParams);
- }
- public String httpGet(String url, List<BasicNameValuePair> parameters) {
- DefaultHttpClient client = getHttpClient();// 默认会到池中查询可用的连接,如果没有就新建
- HttpGet getMethod = null;
- String returnValue = "";
- try {
- getMethod = new HttpGet(url);
- if (null != parameters) {
- String params = EntityUtils.toString(new UrlEncodedFormEntity(
- parameters, DEFAULT_ENCODING));
- getMethod.setURI(new URI(getMethod.getURI().toString() + "?"
- + params));
- logger.debug("httpGet-getUrl:{}", getMethod.getURI());
- }
- HttpResponse response = client.execute(getMethod);
- int statusCode = response.getStatusLine().getStatusCode();
- if (statusCode == 200) {
- HttpEntity he = response.getEntity();
- returnValue = new String(EntityUtils.toByteArray(he),
- DEFAULT_ENCODING);
- return returnValue;
- }
- } catch (UnsupportedEncodingException e) {
- logger.error(Thread.currentThread().getName()
- + "httpGet Send Error,Code error:" + e.getMessage());
- } catch (ClientProtocolException e) {
- logger.error(Thread.currentThread().getName()
- + "httpGet Send Error,Protocol error:" + e.getMessage());
- } catch (IOException e) {
- logger.error(Thread.currentThread().getName()
- + "httpGet Send Error,IO error:" + e.getMessage());
- } catch (URISyntaxException e) {
- logger.error(Thread.currentThread().getName()
- + "httpGet Send Error,IO error:" + e.getMessage());
- } finally {// 释放连接,将连接放回到连接池
- getMethod.releaseConnection();
- }
- return returnValue;
- }
- public String httpPost(String url, List<BasicNameValuePair> parameters,
- String requestBody) {
- DefaultHttpClient client = getHttpClient();// 默认会到池中查询可用的连接,如果没有就新建
- HttpPost postMethod = null;
- String returnValue = "";
- try {
- postMethod = new HttpPost(url);
- if (null != parameters) {
- String params = EntityUtils.toString(new UrlEncodedFormEntity(
- parameters, DEFAULT_ENCODING));
- postMethod.setURI(new URI(postMethod.getURI().toString() + "?"
- + params));
- logger.debug("httpPost-getUrl:{}", postMethod.getURI());
- }
- if (StringUtils.isNotBlank(requestBody)) {
- StringEntity se = new StringEntity(requestBody,
- DEFAULT_ENCODING);
- postMethod.setEntity(se);
- }
- HttpResponse response = client.execute(postMethod);
- int statusCode = response.getStatusLine().getStatusCode();
- if (statusCode == 200) {
- HttpEntity he = response.getEntity();
- returnValue = new String(EntityUtils.toByteArray(he),
- DEFAULT_ENCODING);
- return returnValue;
- }
- } catch (UnsupportedEncodingException e) {
- logger.error(Thread.currentThread().getName()
- + "httpPost Send Error,Code error:" + e.getMessage());
- } catch (ClientProtocolException e) {
- logger.error(Thread.currentThread().getName()
- + "httpPost Send Error,Protocol error:" + e.getMessage());
- } catch (IOException e) {
- logger.error(Thread.currentThread().getName()
- + "httpPost Send Error,IO error:" + e.getMessage());
- } catch (URISyntaxException e) {
- logger.error(Thread.currentThread().getName()
- + "httpPost Send Error,IO error:" + e.getMessage());
- } finally {// 释放连接,将连接放回到连接池
- postMethod.releaseConnection();
- // 释放池子中的空闲连接
- // client.getConnectionManager().closeIdleConnections(30L,
- // TimeUnit.MILLISECONDS);
- }
- return returnValue;
- }
- }
一些参数的解释:
- socketTimeout:socket超时时间,这个超时时间不是连接的超时时间,而是读取返回值的超时时间,即响应超时时间
- connectTimeout:请求连接对方的超时时间
- maxConnNum: 连接池最大并发数。这个设置与应用的并发量有关。比如应用的1s的请求量1000,每个请求需要10ms,则需要的最大并发数为 1000/(1s/10ms)=10,理论上设置的值最好比这个稍大一点点。
- maxPerRoute:请求后端的最大连接数,比如说后端服务有2台机器,而maxConnNum=10,则maxPerRoute=5
根据如上的配置理论上最多只可能会存在4000个连接,对于空闲的连接连接池会根据默认的设置进行回收,当然也可以在程序中进行回收,回收方法client.getConnectionManager().closeIdleConnections(30000L,TimeUnit.MILLSECONDS)表示这个连接维持30秒的空闲时间后进行回收。如果我们将30000L改为0L,则表示立即进行回收,那就失去了连接池的意义,不建议这样做。
【转】同步的HttpClient使用详解的更多相关文章
- HttpClient使用详解(转)
HttpClient使用详解 Http协议的重要性相信不用我多说了,HttpClient相比传统JDK自带的URLConnection,增加了易用性和灵活性(具体区别,日后我们再讨论),它不仅是客户 ...
- HttpClient使用详解
http://itindex.net/detail/52566-httpclient HttpClient使用详解 标签: httpclient | 发表时间:2015-01-22 12:07 | 作 ...
- 剑指Offer——线程同步volatile与synchronized详解
(转)Java面试--线程同步volatile与synchronized详解 0. 前言 面试时很可能遇到这样一个问题:使用volatile修饰int型变量i,多个线程同时进行i++操作,这样可以实现 ...
- Java进阶(三十二) HttpClient使用详解
Java进阶(三十二) HttpClient使用详解 Http协议的重要性相信不用我多说了,HttpClient相比传统JDK自带的URLConnection,增加了易用性和灵活性(具体区别,日后我们 ...
- gvoory脚本中关于HttpClient使用详解实例
一.gvoory脚本中关于HttpClient使用详解实例 HttpClient:是一个接口 首先需要先创建一个DefaultHttpClient的实例 HttpClient httpClient=n ...
- 无锁的同步策略——CAS操作详解
目录 1. 从乐观锁和悲观锁谈起 2. CAS详解 2.1 CAS指令 2.3 Java中的CAS指令 2.4 CAS结合失败重试机制进行并发控制 3. CAS操作的优势和劣势 3.1 CAS相比独占 ...
- HttpClient请求详解
HttpClient 是 Apache Jakarta Common 下的子项目,可以用来提供高效的.最新的.功能丰富的支持 HTTP 协议的客户端编程工具包,并且它支持 HTTP 协议最新的版本和建 ...
- rsync文件同步、Inotify-tools参数详解
inotifywait用于等待文件或文件集上的一个待定事件,可以监控任何文件和目录设置,并且可以递归地监控整个目录树: inotifywatch用于收集被监控的文件系统计数据,包括每个inotify事 ...
- HttpClient类详解
文章链接:https://blog.csdn.net/justry_deng/article/details/81042379 HTTP 协议可能是现在 Internet 上使用得最多.最重要的协议了 ...
随机推荐
- 在Spark shell中基于Alluxio进行wordcount交互式分析
Spark是一个分布式内存计算框架,可部署在YARN或者MESOS管理的分布式系统中(Fully Distributed),也可以以Pseudo Distributed方式部署在单个机器上面,还可以以 ...
- 编程中经常看到上下文context,这个上下文指得是什么?
举个栗子:小美气呼呼对我说:“你去死吧”,我当时哭了. 场景1:小美刚转学到我们学校,我暗恋了她很久,有一天鼓足勇气,向她表白,小美气呼呼对我说:“你去死吧”,我当时就哭了.场景2我跟小美从小青梅竹马 ...
- Oracle服务器重命名
转载自:http://www.codesec.net/view/120387.html 因工作需要准备了一台服务器专门按照oracle数据库,并且服务器上安装了windows 2008R2操作系统,在 ...
- node.js中的模板引擎jade、handlebars、ejs
使用node.js的Express脚手架生成项目默认是jade模板引擎,jade引擎实在是太难用了,这么难用还敢设为默认的模板引擎,过分了啊!用handlebars模板引擎写还说的过去,但笔者更愿意使 ...
- initBinder转换日期格式
@Controller public class FirstController { @RequestMapping("/first") //类型转化工作一定是在真正的handle ...
- rest-framework框架——APIView和序列化组件
一.快速实例 Quickstart http://www.cnblogs.com/yuanchenqi/articles/8719520.html restful协议 ---- 一切皆是资源,操作只是 ...
- css居中那些事
一.css垂直居中 1.line-height(适用于单行文本居中) eg: html:<p class="wordp">123</p>- css: .w ...
- JVM crash at ForUtil.readBlock
今天同学让帮忙看下JVM错误日志,才发现已经开始接触java3个月,还没看到相关错误日志.平时看的都只是程序运行时写入的日志,关于JVM的错误日志还真没看过.网上收集资料,整理如下. 一.日志文件: ...
- sass(混合mixin的调用、@content)
sass中使用@mixin声明混合,可以传递参数,参数名以$符号开始,多个参数以逗号分开,也可以给参数设置默认值.声明的@mixin通过@include来调用 1.无参数mixin scss.styl ...
- [原创]vs2012创建的ado.net模型无法实例化的问题
最近从vs2010升级到vs2012,建立数据模型,发现生成的东西跟以前不一样了,而且也无法实例化使用.百度尝试了n种关键词,终于被我找到解决的方法.在这里记录一下. 1.打开设计器,也就是双击这个 ...