asynchttpclient.java_java之httpClient 3.x、AsyncHttpClient1.9.x使用总结

首先请大牛们见谅菜鸟重复造轮子的学习方式,本文适合新手看~

下面使用的同步http是HttpClient 3.X的版本,不过早已不在维护,如果刚开始使用http,建议大家都换成4.X版本,别看下面的有关同步http的部分了,4.x效率有质地提高,总结3.X只是因为无奈旧项目还在使用。后面再更新一篇有关4.x的,最新的HttpClient 4.X官方地址:http://hc.apache.org/httpcomponents-client-4.5.x/index.html

但鉴于可能有些旧的系统还是采用3.X版本的HttpClient,所以本文还是先记录下使用方法。

http使用场景很多,据以往经验,对于客户端来说,我们使用http一般会发出以下几种常见的场景:

以get方式请求服务器不带任何参数

带上key-value对

以post方式请求服务器不带任何参数

带上key-value对

带上字节数组

带上文件

带上文件+key-value对

以上的场景一般可以满足一般的需求,然后,我们可以在这基础上扩展一点点:假如遇到一个类似于报表的子系统,主系统要在关键的逻辑链路中“打点”,通过http调用报表子系统记录一些相关的信息时,那么如果我们使用同步http来请求报表子系统的话,一旦报表子系统挂了,那么肯定会影响到主系统的运行。

为了不影响到主系统的运行,我们可以采用“异步” 的方式通过http(AsyncHttpClient )请求报表子系统,那么即使子系统挂了,对主系统的关键链路的执行也不会产生多大的影响。所以,封装一个http组件,自然而然少不了封装异步http请求。而异步http所能够做的事情,也应该覆盖上面提到的几种场景。

再者,考虑到效率问题,除非有足够的理由,否则每次调用http接口,都创建立一个新的连接,是相当没效率的,所以MultiThreadedHttpConnectionManager 诞生了,HttpClient在内部维护一个连接池,通过MultiThreadedHttpConnectionManager 我们可以设置“默认连接数”、“最大连接数”、“连接超时”、“读取数据超时”等等配置,从而来提高效率。

废话完了,怎么实现以上需求呢。

包的引用:

同步的http我使用的是org.apache.commons.httpclient的HttpClient的3.1版本。

maven配置为:

commons-httpclient

commons-httpclient

3.1

异步的http我使用的是com.ning.http.client的AsyncHttpClien的1.9.8版本。注意,其需要依赖几个日志相关的组件、分别为log4j、slf4j、slf4j-log4j

maven配置为:

org.slf4j

slf4j-log4j12

1.7.7

org.slf4j

slf4j-api

1.7.5

log4j

log4j

1.2.16

com.ning

async-http-client

1.9.8

为了实现连接池,我们通过一个工厂类来生成httpClient,为了上一层方便调用,我们定义了一个接口,规范了同步、异步http应该实现的方法。包结构如下:

80fe1b4914c42f51d26621c77df7e3eb.png

一、同步的HttpClient 3.X

从工厂入手,工厂负责初始化httpClient的配置,包括“默认连接数”、“最大连接数”、“连接超时”、“读取数据超时”等等,不同的服务我们应该创建不同的manager,因为不可能我们调服务A和调服务B使用同一套配置是吧,比如超时时间,应该考虑会有所差异。初始化完配置后,把 manager传到实现类,在实现类中new HttpClient。

工厂代码如下:

//专门针对xx服务器的连接管理对象//因为不同服务可能超时等参数不用,所以针对不同服务,把连接管理对象区分开来,这只是其中一个

private static MultiThreadedHttpConnectionManager xxconnectionManager = newMultiThreadedHttpConnectionManager();static{//专门针对xx服务器的连接参数

xxconnectionManager = newMultiThreadedHttpConnectionManager();

HttpConnectionManagerParams paramsSearch= newHttpConnectionManagerParams();

paramsSearch.setDefaultMaxConnectionsPerHost(1000); //默认连接数

paramsSearch.setMaxTotalConnections(1000); //最大连接数

paramsSearch.setConnectionTimeout(30000); //连接超时

paramsSearch.setSoTimeout(20000); //读数据超时

xxconnectionManager.setParams(paramsSearch);

}/** 返回针对XX服务的httpClient包装类*/

public staticSyncHttpClientWapperImpl getXXSearchHttpClient() {return newSyncHttpClientWapperImpl(xxconnectionManager);

}

注意一点,这些连接数,超时等的配置,要做要调查工作之后再定夺,是根据访问服务的不同,我们自己的机器能有多少剩余的可用空间的不同而不同的,而不是随随便便就设置一个参数。

实现类的构造方法如下:

private HttpClient client;//httpClient

private final static String CHARACTER = "UTF-8";//构造器,由工厂调用

publicSyncHttpClientWapperImpl(MultiThreadedHttpConnectionManager connectionManager) {

client= newHttpClient(connectionManager);//字符集

client.getParams().setParameter(HttpMethodParams.HTTP_CONTENT_CHARSET, CHARACTER);

}

这里有一个挺困惑的点:HttpClient有必要弄成静态的吗?即直接在工厂里面为每种服务生成一个静态的HttpClient,然后传到实现类?经测试,改成静态的效率并没有提高,在文件传输的测试中,甚至下降了,这个有点困惑,大家可以试一试一起讨论一下。

然后,在实现类中实现各种方法。

第一种,通过URL,以get方式请求服务器,返回字节数组。

public byte[] getWithQueryURL(String queryURL) throwsHttpClientException {if(queryURL == null) {throw new HttpClientException("queryURL is null.");

}byte[] newbuf =executeByGet(queryURL);if ((newbuf == null) || (newbuf.length == 0)) {throw new HttpClientException("Server response is null: " +queryURL);

}returnnewbuf;

}private byte[] executeByGet(String url) throwsHttpClientException {

HttpMethod method= newGetMethod(url);//RequestHeader

method.setRequestHeader("Content-type" , "text/html; charset=UTF-8");//提交请求

try{

client.executeMethod(method);

}catch(Exception e) {

method.releaseConnection();throw newHttpClientException(url, e);

}//返回字节流

byte[] responseBody = null;try{

responseBody=getBytesFromInpuStream(method.getResponseBodyAsStream());

}catch(IOException e) {throw newHttpClientException(e);

}finally{

method.releaseConnection();

}returnresponseBody;

}

接着,写一个通用的流解析方法,负责把返回的流解析成字节数组。

private byte[] getBytesFromInpuStream(InputStream instream) throwsIOException {

ByteArrayOutputStream outstream= newByteArrayOutputStream();try{intlength;byte[] tmp = new byte[8096];while ((length = instream.read(tmp)) != -1) {

outstream.write(tmp,0, length);

}returnoutstream.toByteArray();

}finally{

instream.close();

outstream.close();

}

}

这样就完成了最简单的get请求的调用了。

第二种:通过URL和paramsMap参数,以post方式请求服务器,返回字节数组。

public byte[] postWithParamsMap( String queryURL, Map paramsMap) throwsHttpClientException{if(queryURL == null) {throw new HttpClientException("queryURL is null.");

}byte[] newbuf =executeByPostWithParamsMap(queryURL,paramsMap);if ((newbuf == null) || (newbuf.length == 0)) {throw new HttpClientException("Server response is null: " +queryURL);

}returnnewbuf;

}private byte[] executeByPostWithParamsMap(String URL, Map paramsMap) throwsHttpClientException {

PostMethod method= newPostMethod(URL);//构造参数

if(paramsMap != null) {

Set> entrySet =paramsMap.entrySet();

Iterator> iterator =entrySet.iterator();

NameValuePair[] nvps= newNameValuePair[paramsMap.size()];int i = 0;while(iterator.hasNext()) {

Entry entry =iterator.next();if(entry.getKey() != null) {

NameValuePair nvp= newNameValuePair(entry.getKey(),entry.getValue());

nvps[i++] =nvp;

}

}

method.setRequestBody(nvps);

}//RequestHeader,key-value对的话,httpClient自动带上application/x-www-form-urlencoded

method.setRequestHeader("Content-type" , "application/x-www-form-urlencoded; charset=UTF-8");//提交请求

try{

client.executeMethod(method);

}catch(Exception e) {

method.releaseConnection();throw newHttpClientException(URL, e);

}//返回字节流

byte[] responseBody = null;try{

responseBody=getBytesFromInpuStream(method.getResponseBodyAsStream());

}catch(IOException e) {throw newHttpClientException(e);

}finally{

method.releaseConnection();

}returnresponseBody;

}

第三种:通过URL和bytes参数,以post方式请求服务器,返回字节数组。

public byte[] postWithBytes(String queryURL , byte[] bytes) throwsHttpClientException{if(queryURL == null) {throw new HttpClientException("queryURL is null.");

}byte[] newbuf =executeByPostWithBytes(queryURL,bytes);if ((newbuf == null) || (newbuf.length == 0)) {throw new HttpClientException("Server response is null: " +queryURL);

}returnnewbuf;

}private byte[] executeByPostWithBytes(String queryURL, byte[] bytes) throwsHttpClientException {

PostMethod method= newPostMethod(queryURL);

RequestEntity requestEntity= newByteArrayRequestEntity(bytes);

method.setRequestEntity(requestEntity);//RequestHeader

method.setRequestHeader("Content-type" , "text/plain; charset=UTF-8");//提交请求

try{

client.executeMethod(method);

}catch(Exception e) {

method.releaseConnection();throw newHttpClientException(queryURL, e);

}//返回字节流

byte[] responseBody = null;try{

responseBody=getBytesFromInpuStream(method.getResponseBodyAsStream());

}catch(IOException e) {throw newHttpClientException(e);

}finally{

method.releaseConnection();

}returnresponseBody;

}

第四种:通过URL、fileList、paramMap参数,以post方式请求服务器,返回字节数组。

public byte[] postWithFileListAndParamMap(String queryURL,List fileList,Map paramMap) throwsHttpClientException, HttpException, IOException {if(queryURL == null) {throw new HttpClientException("queryURL is null.");

}if(fileList == null) {throw new HttpClientException("file is null.");

}if(paramMap == null){throw new HttpClientException("paramMap is null.");

}returnexecuteByPostWithFileListAndParamMap(queryURL, fileList, paramMap);

}private byte[] executeByPostWithFileListAndParamMap (String queryURL,List fileList,Map paramMap) throwsHttpException, IOException, HttpClientException {if(queryURL != null && fileList != null && fileList.size() > 0) {//post方法

PostMethod method = newPostMethod(queryURL);//Part[]

Part[] parts = null;if(paramMap != null) {

parts= new Part[fileList.size()+paramMap.size()];

}else{

parts= newPart[fileList.size()];

}int i = 0;//FilePart

for(File file : fileList){

Part filePart= newFilePart(file.getName(),file);

parts[i++] =filePart;

}//StringPart

if(paramMap != null) {

Set> entrySet =paramMap.entrySet();

Iterator> it =entrySet.iterator();while(it.hasNext()) {

Entry entry =it.next();

Part stringPart= newStringPart(entry.getKey(),entry.getValue(),CHARACTER);

parts[i++] =stringPart;

}

}//Entity

RequestEntity requestEntity = newMultipartRequestEntity(parts, method.getParams());

method.setRequestEntity(requestEntity);//RequestHeader,文件的话,HttpClient自动加上multipart/form-data//method.setRequestHeader("Content-type" , "multipart/form-data; charset=UTF-8");//excute

try{

client.executeMethod(method);

}catch(Exception e) {

method.releaseConnection();throw newHttpClientException(queryURL, e);

}//return

byte[] responseBody = null;try{

responseBody=getBytesFromInpuStream(method.getResponseBodyAsStream());

}catch(IOException e) {throw newHttpClientException(e);

}finally{

method.releaseConnection();

}returnresponseBody;

}return null;

}

注意Part stringPart = new StringPart(entry.getKey(),entry.getValue(),CHARACTER);这句代码,一定要加上编码格式,不然服务端会乱码。

二、异步的AsyncHttpClient

同样的,按照这种思路,异步的AsyncHttpClient也有类似的实现,不过写法不同而已,在工厂中,AsyncHttpClient使用的是AsyncHttpClientConfig.Builder作为管理配置的类,也有类似连接超时,最大连接数等配置。

工厂类:

//专门针对xx服务器的连接管理对象//因为不同服务可能超时等参数不用,所以针对不同服务,把连接管理对象区分开来,这只是其中一个

private static AsyncHttpClientConfig.Builder xxbuilder = newAsyncHttpClientConfig.Builder();static{

xxbuilder.setConnectTimeout(3000); //连接超时

xxbuilder.setReadTimeout(2000); //读取数据超时

xxbuilder.setMaxConnections(1000); //最大连接数

}/** 返回针对XX服务的httpClient包装类*/

public staticAsyncHttpClientWapperImpl getXXSearchHttpClient() {return newAsyncHttpClientWapperImpl(xxbuilder);

}

其使用了builder 的设计模式,活生生的一个例子,值得学习。

实现类的构造方法:

privateAsyncHttpClient client;publicAsyncHttpClientWapperImpl(Builder xxbuilder) {

client= newAsyncHttpClient(xxbuilder.build());

}

这样,AsyncHttpClient对象就创建完毕了。接下来是各种场景的实现,感觉异步的AsyncHttpClient封装得比HttpClient 3.X更加容易使用,设计得更好。

第一种:通过URL,以get方式请求服务器,返回字节数组。

public byte[] getWithQueryURL(String queryURL)throwsHttpClientException, HttpClientException {if(queryURL == null) {throw new HttpClientException("queryURL为空");

}byte[] newbuf =executeByGet(queryURL);if ((newbuf == null) || (newbuf.length == 0)) {throw new HttpClientException("Server response is null: " +queryURL);

}returnnewbuf;

}private byte[] executeByGet(String queryURL) throwsHttpClientException {byte[] responseBody = null;try{

Future f =client.prepareGet(queryURL).execute();

responseBody=getBytesFromInpuStream(f.get().getResponseBodyAsStream());

}catch(Exception e) {throw newHttpClientException(e);

}returnresponseBody;

}

同样的,我们写了一个getBytesFromInputStream()方法解析服务端返回的流,我们发现,两个实现类里面都有一些共同的方法,这里可以考虑写一个父类,把这些方法提取出来。

第二种:通过URL和paramsMap参数,以post方式请求服务器,返回字节数组。

public byte[] postWithParamsMap(String queryURL,

Map paramsMap) throwsHttpClientException {if(queryURL == null) {throw new HttpClientException("queryURL为空");

}byte[] newbuf =executeByPostByParamMap(queryURL,paramsMap);if ((newbuf == null) || (newbuf.length == 0)) {throw new HttpClientException("Server response is null: " +queryURL);

}returnnewbuf;

}private byte[] executeByPostByParamMap(String queryURL,Map paramsMap) throwsHttpClientException {byte[] responseBody = null;try{

RequestBuilder requestBuilder= newRequestBuilder();//添加 key-value参数

if(paramsMap != null && paramsMap.size() > 0) {

Set> entrySet =paramsMap.entrySet();

Iterator> iterator =entrySet.iterator();while(iterator.hasNext()) {

Entry entry =iterator.next();if(entry.getKey() != null) {

requestBuilder.addFormParam(entry.getKey(), entry.getValue());

}

}

}//添加RequestHeader,key

requestBuilder.addHeader("Content-type", "application/x-www-form-urlencoded; charset=UTF-8");

requestBuilder.setMethod("POST");//添加URL

requestBuilder.setUrl(queryURL);//request

Request request =requestBuilder.build();//提交

ListenableFuture f =client.executeRequest(request);

responseBody=getBytesFromInpuStream(f.get().getResponseBodyAsStream());

}catch(Exception e) {throw newHttpClientException(e);

}returnresponseBody;

}

第三种:通过URL和bytes参数,以post方式请求服务器,返回字节数组。

public byte[] postWithBytes(String queryURL, byte[] bytes)throwsHttpClientException {if(queryURL == null) {throw new HttpClientException("queryURL is null.");

}byte[] newbuf =executeByPostWithBytes(queryURL,bytes);if ((newbuf == null) || (newbuf.length == 0)) {throw new HttpClientException("Server response is null: " +queryURL);

}returnnewbuf;

}private byte[] executeByPostWithBytes(String queryURL, byte[] bytes) throwsHttpClientException {byte[] responseBody = null;try{

RequestBuilder requestBuilder= newRequestBuilder();//添加 bytes参数

requestBuilder.setBody(bytes);//添加RequestHeader,key

requestBuilder.addHeader("Content-type", "text/plain; charset=UTF-8");

requestBuilder.setMethod("POST");//添加URL

requestBuilder.setUrl(queryURL);//request

Request request =requestBuilder.build();//提交

ListenableFuture f =client.executeRequest(request);

responseBody=getBytesFromInpuStream(f.get().getResponseBodyAsStream());

}catch(Exception e) {throw newHttpClientException(e);

}returnresponseBody;

}

第四种:通过URL、fileList、paramMap参数,以post方式请求服务器,返回字节数组。

public byte[] postWithFileListAndParamMap(String queryURL,

List fileList, MapparamMap)throwsHttpClientException, HttpException, IOException {if(queryURL == null) {throw new HttpClientException("queryURL is null.");

}if(fileList == null || fileList.size() == 0) {throw new HttpClientException("fileList is null.");

}if(paramMap == null || paramMap.size() == 0) {throw new HttpClientException("paramMap is null.");

}returnexecuteByPostWithFileListAndParamMap(queryURL, fileList, paramMap);

}private byte[] executeByPostWithFileListAndParamMap (String queryURL,List fileList,Map paramsMap) throwsHttpException, IOException, HttpClientException {if(queryURL != null && fileList != null && fileList.size() > 0) {byte[] responseBody = null;try{

RequestBuilder requestBuilder= newRequestBuilder();//FilePart

for(File file : fileList){

Part filePart= newFilePart(file.getName(),file);

requestBuilder.addBodyPart(filePart);

}//StringPart

if(paramsMap != null) {

Set> entrySet =paramsMap.entrySet();

Iterator> it =entrySet.iterator();while(it.hasNext()) {

Entry entry =it.next();

Part stringPart= newStringPart(entry.getKey(),entry.getValue());

requestBuilder.addBodyPart(stringPart);

}

}//添加RequestHeader,key

requestBuilder.addHeader("Content-type", "multipart/form-data; charset=UTF-8");

requestBuilder.setMethod("POST");//添加URL

requestBuilder.setUrl(queryURL);//request

Request request =requestBuilder.build();//提交

ListenableFuture f =client.executeRequest(request);

responseBody=getBytesFromInpuStream(f.get().getResponseBodyAsStream());

}catch(Exception e) {throw newHttpClientException(e);

}returnresponseBody;

}return null;

}

OK,入了个门后,更多的用法可以自己去看文档了,请不要局限以上几种常用的场景。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值