【转】同步的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 上使用得最多.最重要的协议了 ...
随机推荐
- 华为 p8上配置outllook,同步日历/邮件
在网上找了半天,也没有找到合适的方法,在outlook官网上终于找到了配置方式 一种方式是使用outlook的手机应用.(此处省略) 另外一种方式是使用手机自带的邮件/日历应用(见下面操作方法) 如果 ...
- Docker镜像命名解析
镜像是Docker最核心的技术之一,也是应用发布的标准格式. 无论你是用docker pull image,或者是在Dockerfile里面写FROM image, 从Docker官方Registry ...
- (转)PowerHA完全手册(一,二,三)
PowerHA完全手册(一) 原文:http://www.talkwithtrend.com/Article/39889-----PowerHA完全手册(一) http://www.talkwitht ...
- 微博关系服务与Redis的故事
http://www.infoq.com/cn/articles/weibo-relation-service-with-redis?utm_source=articles_about_Redis&a ...
- c#特性类 Attribute
Attribute FYI Link: Attribute在.net编程中的应用(一) Attribute在.net编程中的应用(二) Attribute在.net编程中的应用(三) Attribut ...
- s-2、charles 入门
本文的内容主要包括: Charles 的简介 如何安装 Charles 将 Charles 设置成系统代理 Charles 主界面介绍 过滤网络请求 截取 iPhone 上的网络封包 截取 Https ...
- nyoj 1239——引水工程——————【最小生成树 prim】
引水工程 时间限制:2000 ms | 内存限制:65535 KB 难度:3 描述 南水北调工程是优化水资源配置.促进区域协调发展的基础性工程,是新中国成立以来投资额最大.涉及面最广的战略性工 ...
- Python sh模块--------替换subprocess的利器
官方文档有句话"allows you to call any program",并且: helps you write shell scripts in Python by giv ...
- CssClass初步语法了解
首先 创建Css有三种方法 这里面就不一一介绍了,主要说第二种 创建第二种Css样式表 要在标签<title><title>标签下面写 如: <style type= ...
- Android的Overlay机制
相关知识点的掌握: AAPT的使用和原理 编译脚本 参考:http://blog.sina.com.cn/s/blog_645b74b90101ojkc.html