这里有两种封装,分别是:
1:建立异步Http请求客户端之后,所有http请求都复用该HttpAsyncClient,内部只对应一个Reactor I/O线程。
请求的回调函数中不关闭HttpAsyncClient。
2:每次请求都建立一个新的HttpAsyncClient,请求回调结束后关闭HttpAsyncClient,释放资源。
在我的项目中,我选择第一种:复用单例HttpAsyncClient,因为一个Reactor I/O线程其实可以
同时处理成千上万个连接请求,完全满足我的项目需要。
一、单例HttpAsyncClient
package com.peas.pay.util;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.config.RequestConfig;
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.client.methods.HttpUriRequest;
import org.apache.http.concurrent.FutureCallback;
import org.apache.http.impl.nio.client.CloseableHttpAsyncClient;
import org.apache.http.impl.nio.client.HttpAsyncClients;
import org.apache.http.impl.nio.reactor.IOReactorConfig;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;
import com.google.common.base.Preconditions;
import com.google.common.collect.Lists;
import lombok.extern.log4j.Log4j;
/**
* Created by xiekunliang on 2018/3/24. 异步Http请求封装工具类
*/
@Log4j
public class AsyncHttpClientUtil
{
private static CloseableHttpAsyncClient httpClient;
private static volatile boolean isClientStart;
/**
* 创建CloseableHttpAsyncClient
* @return
*/
private static CloseableHttpAsyncClient createCustomAsyncClient()
{
Preconditions.checkState(!isClientStart, "客户端HttpAsyncClient已经建立过了");
IOReactorConfig ioReactorConfig = IOReactorConfig.custom()
.setIoThreadCount(Runtime.getRuntime().availableProcessors())
.setConnectTimeout(60000)
.setSoTimeout(60000)
.build();
// 设置超时时间 毫秒为单位
RequestConfig requestConfig = RequestConfig
.copy(RequestConfig.DEFAULT)
.setConnectTimeout(60000)
.build();
return HttpAsyncClients
.custom()
.setDefaultIOReactorConfig(ioReactorConfig)
.setDefaultRequestConfig(requestConfig)
.build();
}
public static void startHttpClient()
{
httpClient = createCustomAsyncClient();
httpClient.start();
isClientStart = true;
}
public static void closeHttpClient()
{
try {
httpClient.close();
} catch (IOException e) {
e.printStackTrace();
}
isClientStart = false;
}
public static void http(String method,
String url,
Object parameter,
StringFutureCallback callback)
{
Preconditions.checkNotNull(method);
if ("GET".equals(method))
{
get(url, callback);
}
else if ("POST".equals(method))
{
post(url, parameter, callback);
}
}
public static void get(String url, StringFutureCallback callback)
{
Preconditions.checkArgument(isClientStart, "还没有建立Http Client");
HttpUriRequest request = new HttpGet(url);
httpClient.execute(request, new DefaultFutureCallback(callback));
}
public static void post(String url, Object parameter, StringFutureCallback callback)
{
Preconditions.checkArgument(isClientStart, "还没有建立Http Client");
HttpPost httpPost = new HttpPost(url);
if (parameter != null)
{
List pairs = Lists.newArrayList();
UrlEncodedFormEntity entity = null;
try {
if (parameter instanceof HashMap)
{
Map parameters = (Map) parameter;
parameters.forEach((k, v) -> pairs.add(new BasicNameValuePair(k,v)));
entity = new UrlEncodedFormEntity(pairs, "UTF-8");
}
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
httpPost.setEntity(entity);
}
httpClient.execute(httpPost, new DefaultFutureCallback(callback));
}
/**
* 字符串类型结果回调
*/
public interface StringFutureCallback
{
void success(String content);
}
public static class DefaultFutureCallback implements FutureCallback
{
private StringFutureCallback callback;
public DefaultFutureCallback(StringFutureCallback callback)
{
this.callback = callback;
}
@Override
public void completed(HttpResponse httpResponse)
{
HttpEntity entity = httpResponse.getEntity();
String content = "";
try
{
content = EntityUtils.toString(entity, "UTF-8");
}
catch (IOException e)
{
e.printStackTrace();
}
callback.success(content);
}
@Override
public void failed(Exception e)
{
e.printStackTrace();
}
@Override
public void cancelled()
{
log.debug("http request cancelled");
}
}
}
二、每次都新建立一个HttpAsyncClient
package com.peas.pay.util;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.config.RequestConfig;
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.client.methods.HttpUriRequest;
import org.apache.http.concurrent.FutureCallback;
import org.apache.http.impl.nio.client.CloseableHttpAsyncClient;
import org.apache.http.impl.nio.client.HttpAsyncClients;
import org.apache.http.impl.nio.reactor.IOReactorConfig;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;
import com.google.common.base.Preconditions;
import com.google.common.collect.Lists;
/**
* Created by xiekunliang on 2018/3/24. 异步Http请求封装工具类
*/
public class AsyncHttpClientUtil
{
public interface StringFutureCallback
{
void success(String content);
}
public static void http(String method, String url, Object parameter, StringFutureCallback callback)
{
Preconditions.checkNotNull(method);
if ("GET".equals(method))
{
get(url, callback);
}
else if ("POST".equals(method))
{
post(url, parameter, callback);
}
}
private static CloseableHttpAsyncClient createCustomAsyncClient() {
IOReactorConfig ioReactorConfig = IOReactorConfig.custom()
.setIoThreadCount(Runtime.getRuntime().availableProcessors())
.setConnectTimeout(60000)
.setSoTimeout(60000)
.build();
// 设置超时时间 毫秒为单位
RequestConfig requestConfig = RequestConfig.copy(RequestConfig.DEFAULT).setConnectTimeout(60000).build();
return HttpAsyncClients.custom().setDefaultIOReactorConfig(ioReactorConfig).setDefaultRequestConfig(requestConfig).build();
}
public static void get(String url, StringFutureCallback callback)
{
CloseableHttpAsyncClient asyncClient = createCustomAsyncClient();
asyncClient.start();
HttpUriRequest request = new HttpGet(url);
asyncClient.execute(request, new DefaultFutureCallback(asyncClient, callback));
}
public static void post(String url, Object parameter, StringFutureCallback callback)
{
CloseableHttpAsyncClient asyncClient = createCustomAsyncClient();
asyncClient.start();
HttpPost httpPost = new HttpPost(url);
if (parameter != null)
{
List pairs = Lists.newArrayList();
UrlEncodedFormEntity entity = null;
try {
if (parameter instanceof HashMap)
{
Map parameters = (Map) parameter;
parameters.forEach((k, v) -> pairs.add(new BasicNameValuePair(k,v)));
entity = new UrlEncodedFormEntity(pairs, "UTF-8");
}
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
httpPost.setEntity(entity);
}
asyncClient.execute(httpPost, new DefaultFutureCallback(asyncClient, callback));
}
public static class DefaultFutureCallback implements FutureCallback
{
private CloseableHttpAsyncClient client;
private StringFutureCallback callback;
public DefaultFutureCallback(CloseableHttpAsyncClient client, StringFutureCallback callback)
{
this.client = client;
this.callback = callback;
}
@Override
public void completed(HttpResponse httpResponse)
{
HttpEntity entity = httpResponse.getEntity();
String content = "";
try
{
content = EntityUtils.toString(entity, "UTF-8");
}
catch (IOException e)
{
e.printStackTrace();
}
try
{
callback.success(content);
}
finally
{
try
{
client.close();
}
catch (IOException e1)
{
e1.printStackTrace();
}
}
}
@Override
public void failed(Exception e)
{
e.printStackTrace();
Thread t1 = new Thread(() -> {
try {
// 发生连接超时等异常时,failed回调执行到client.close()方法;
// 但是可能会在this.reactorThread.join()这里无限期阻塞。
// 可能哪个地方形成了死锁,所以这里用一个超时中断,结束Reactor I/O线程的死锁。
client.close();
} catch (IOException e1) {
e1.printStackTrace();
}
});
t1.start();
try {
t1.join(30000);
Thread.currentThread().interrupt();
} catch (InterruptedException e1) {
e1.printStackTrace();
}
}
@Override
public void cancelled()
{
try
{
client.close();
}
catch (IOException e1)
{
e1.printStackTrace();
}
}
}
}
第二种封装代码有可能写的不是很完善,主要是在failed()回调方法中,手动调用client.close()方法后,
经过测试:Reactor I/O线程无限期阻塞,可能是哪里造成了死锁。本代码中为了使线程资源得到释放,采用中断的方式处理。
可能不是很妥当,但是还没有想到其他更好的办法。
希望有经验的大神指正该怎么做。
pom文件主要有:
com.google.guava
guava
19.0
org.projectlombok
lombok
1.16.6
org.apache.httpcomponents
httpclient
4.5
log4j
log4j
1.2.17
org.apache.httpcomponents
httpasyncclient
4.1.3