AsyncHttpClient(AHC)库允许Java应用程序轻松执行HTTP请求并异步处理HTTP响应。 该库还支持WebSocket协议。它的请求处理机制基于netty。作者@TomGranot
基于AsyncHttpClient和CompletableFuture,我编写了一个工具类。
AsyncHttpClient:异步执行请求
CompletableFuture:异步处理结果,并可聚合多个请求结果
完整代码已上传httpAsync
引入
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.asynchttpclient</groupId>
<artifactId>async-http-client-bom</artifactId>
<version>LATEST_VERSION</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.asynchttpclient</groupId>
<artifactId>async-http-client</artifactId>
</dependency>
</dependencies>
异步http工具类
工具类包含一个客户端【HttpAsyncClient】和一个工具类【HttpAsyncUtil】
package com.isszwy.zhyq.util.httpAsync;
import static org.asynchttpclient.Dsl.asyncHttpClient;
import java.io.IOException;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.epoll.EpollEventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import org.asynchttpclient.AsyncHttpClient;
import org.asynchttpclient.AsyncHttpClientConfig;
import org.asynchttpclient.DefaultAsyncHttpClientConfig;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class HttpAsyncClient {
private static Logger log = LoggerFactory.getLogger(HttpAsyncClient.class);
private static final int connectTimeoutMilliseconds = 5000;
private static final int requestTimeoutMilliseconds = 5000;
private static final int readTimeoutTimeoutMilliseconds = 5000;
private static final int maxRequestRetry = 2;
private static EventLoopGroup eventLoopGroup;
private static AsyncHttpClient asyncHttpClient;
private HttpAsyncClient() {
}
public static AsyncHttpClient getInstance() {
if (asyncHttpClient == null) {
synchronized (AsyncHttpClient.class) {
if (asyncHttpClient == null) {
asyncHttpClient = asyncHttpClient(initConfig());
log.info("===HttpAsyncClient初始化完成===");
}
}
}
return asyncHttpClient;
}
private static AsyncHttpClientConfig initConfig() {
String osName = System.getProperty("os.name");
if ("Linux".equalsIgnoreCase(osName)) {
eventLoopGroup = new EpollEventLoopGroup();
} else {
eventLoopGroup = new NioEventLoopGroup();
}
AsyncHttpClientConfig asyncHttpClientConfig = new DefaultAsyncHttpClientConfig
.Builder()
.setConnectTimeout(connectTimeoutMilliseconds)
.setRequestTimeout(requestTimeoutMilliseconds)
.setReadTimeout(readTimeoutTimeoutMilliseconds)
.setEventLoopGroup(eventLoopGroup)
//设置信任所有ssl
.setUseInsecureTrustManager(true)
.setMaxRequestRetry(maxRequestRetry)
.build();
return asyncHttpClientConfig;
}
public static void shutdown() {
if (asyncHttpClient != null) {
try {
asyncHttpClient.close();
eventLoopGroup.shutdownGracefully();
} catch (IOException exception) {
log.error("===HttpAsyncClient关闭异常===",exception);
}
}
}
}
package com.isszwy.zhyq.util.httpAsync;
import static io.netty.handler.codec.http.HttpHeaderValues.APPLICATION_JSON;
import static org.asynchttpclient.Dsl.*;
import io.netty.handler.codec.http.DefaultHttpHeaders;
import io.netty.handler.codec.http.HttpHeaders;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.function.Function;
import org.asynchttpclient.Param;
import org.asynchttpclient.Request;
import org.asynchttpclient.RequestBuilder;
import org.asynchttpclient.Response;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class HttpAsyncUtil {
private static Logger log = LoggerFactory.getLogger(HttpAsyncUtil.class);
/**
* 发送application/x-www-form-urlencoded请求
* @param url
* @return CompletableFuture<String>
*/
public static CompletableFuture<String> doGet(String url) {
return doGet(url, null,null);
}
/**
* 发送application/x-www-form-urlencoded请求
* @param url
* @param param
* @param header
* @return CompletableFuture<String>
*/
public static CompletableFuture<String> doGet(String url, Map<String, String> param,Map<String, String> header) {
log.info("### request url : {} , param : {} , header : {} !",url,param,header);
RequestBuilder request = get(url);
if (header != null) {
HttpHeaders headers = new DefaultHttpHeaders();
for (String key : header.keySet()) {
headers.add(key, header.get(key));
}
request.setHeaders(headers);
}
if (param != null) {
List<Param> paramList = new ArrayList<>();
for (String key : param.keySet()) {
if(param.get(key) !=null){
paramList.add(new Param(key, String.valueOf(param.get(key))));
}
}
request.setFormParams(paramList);
}
CompletableFuture<String> resultFuture= HttpAsyncClient.getInstance().
prepareRequest(request).execute().toCompletableFuture()
.exceptionally(new Function<Throwable, Response>() {
@Override
public Response apply(Throwable throwable) {
System.out.println("exceptionally - apply :" + throwable.toString());
log.error("### send get request error occured !", throwable);
return null;
}
})
.thenApply(response -> {
if (response.getStatusCode() == 200) {
return response.getResponseBody();
}else{
log.error("### get response error occured !", response.getResponseBody());
return null;
}
});
return resultFuture;
}
public static CompletableFuture<String> doPost(String url) {
return doPost(url, null, null);
}
/**
* 发送application/octet-stream请求
* @param url
* @param param
* @param header
* @return CompletableFuture<String>
*/
public static CompletableFuture<String> doPost(String url, Map<String, String> param,Map<String, String> header) {
log.info("### request url : {} , param : {} , header : {} !",url,param,header);
RequestBuilder request = post(url);
if (header != null) {
HttpHeaders headers = new DefaultHttpHeaders();
for (String key : header.keySet()) {
headers.add(key, header.get(key));
}
request.setHeaders(headers);
}
if (param != null) {
List<Param> paramList = new ArrayList<>();
for (String key : param.keySet()) {
if(param.get(key) !=null){
paramList.add(new Param(key, String.valueOf(param.get(key))));
}
}
request.setFormParams(paramList);
}
CompletableFuture<String> resultFuture= HttpAsyncClient.getInstance().
prepareRequest(request).execute().toCompletableFuture()
.exceptionally(new Function<Throwable, Response>() {
@Override
public Response apply(Throwable throwable) {
System.out.println("exceptionally - apply :" + throwable.toString());
log.error("### send Post request error occured !", throwable);
return null;
}
})
.thenApply(response -> {
if (response.getStatusCode() == 200) {
return response.getResponseBody();
}else{
log.error("### get Post response error occured !", response.getResponseBody());
return null;
}
});
return resultFuture;
}
/**
* 发送application/json请求
* @param url
* @param json
* @return CompletableFuture<String>
*/
public static CompletableFuture<String> doPostJson(String url, String json){
Request request = post(url).setBody(json).setHeader("content-type", APPLICATION_JSON).build();
CompletableFuture<String> resultFuture= HttpAsyncClient.getInstance().
prepareRequest(request).execute().toCompletableFuture()
.exceptionally(new Function<Throwable, Response>() {
@Override
public Response apply(Throwable throwable) {
System.out.println("exceptionally - apply :" + throwable.toString());
log.error("### send PostJson request error occured !", throwable);
return null;
}
})
.thenApply(response -> {
if (response.getStatusCode() == 200) {
return response.getResponseBody();
}else{
log.error("### get PostJson response error occured !", response.getResponseBody());
return null;
}
});
return resultFuture;
}
}
使用
CompletableFuture<String> resultHik = HttpAsyncUtil.doPostJson(HIKURL, JSON.toJSONString(paramsMap));
CompletableFuture<String> resultData = HttpAsyncUtil.doPost(url, paramMap, null);
示例
CompletableFuture<String> resultHik = HttpAsyncUtil.doPostJson(HIKURL, JSON.toJSONString(hikParamsMap));
CompletableFuture<String> resultData = HttpAsyncUtil.doPost(url, paramMap, null);
CompletableFuture<List<Map<String,Object>>> endResult = resultData.thenCombine(resultHik, (res1, res2) -> {
Map<String,Object> hikResult = JSON.parseMap(res2);
Map<String,Object> dataResult = JSON.parseMap(res1);
List<Map<String,Object>> result = null;
if(hikResult!= null && "0".equals(hikResult.get("code"))
&& dataResult!= null && "200".equals(dataResult.get("code"))){
Map<String, Object> data = (Map<String, Object>) hikResult.get("data");
List<Map<String,Object>> hikList = (List<Map<String,Object>>) data.get("list");
Map<String,Object> dataDa = (Map<String,Object>) dataResult.get("data");
List<Map<String,Object>> dataList =(List<Map<String, Object>>) dataDa.get("data");
for(Map<String,Object> hikObj:hikList){
for(Map<String,Object> dataObj:dataList){
if(hikObj.get("cameraIndexCode").equals(dataObj.get("CAMERAINDEXCODE"))){
hikObj.put("altitude", dataObj.get("LATITUDE"));
hikObj.put("longitude", dataObj.get("LONGITUDE"));
}
}
}
result = hikList;
}
return result;
});
性能测试
一个主线程循环执行1、2、3、10、20、30次(N)请求,异步和同步请求的响应时间(单位ms,包含结果逻辑处理的时间)。请求执行一次响应时间没有明显变化,请求执行多次,性能有明显提升。
请求次数 | 异步 | 同步 | 性能提升 |
---|---|---|---|
1 | 4048ms | 4688ms | 14% |
2 | 4928ms | 8484ms | 42% |
3 | 5970ms | 11043ms | 46% |
10 | 13583ms | 33147ms | 59% |
20 | 22873ms | 65358ms | 65% |
30 | 38014ms | 96989ms | 61% |
https://github.com/AsyncHttpClient/async-http-client
https://zhuanlan.zhihu.com/p/363832799