HTTP请求异步编程解决堵塞之痛-Apache HttpClient5.0

Posted 老吕架构

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了HTTP请求异步编程解决堵塞之痛-Apache HttpClient5.0相关的知识,希望对你有一定的参考价值。

序言

        相信很多同学用过  Apache HttpClient 组件进行过 http请求,尤其是对第三方系统的http  api  进行调用。在spring cloud 大行其道的今天,微服务之间通过http协议进行调用的场景应用越来越多,今天大黄鸭就 Apache HttpClient5.0的同步、异步、fluent风格、cache插件的使用写个demo,希望可以帮到需要的同学。


一、maven坐标引入

<dependency>
<groupId>org.apache.httpcomponents.client5</groupId>
<artifactId>httpclient5</artifactId>
<version>5.0.3</version>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents.client5</groupId>
<artifactId>httpclient5-fluent</artifactId>
<version>5.0.3</version>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents.client5</groupId>
<artifactId>httpclient5-cache</artifactId>
<version>5.0.3</version>
</dependency>


二、经典的堵塞式编程

/**
* @Description HttpClient用法演示
* @Author lvaolin
* @Date 2021/2/9 4:07 下午
*/
public class HttpClientClassic {
public static void main(String[] args) throws IOException, ParseException {
//关于CloseableHttpClient实例的创建HttpClients提供了多种方式,高级用法可以使用定制方法 HttpClients.custom()
try (CloseableHttpClient httpclient = HttpClients.createDefault()) {
//创建一个get类型的http请求
HttpGet httpGet = new HttpGet("http://www.baidu.com/");
try (CloseableHttpResponse response1 = httpclient.execute(httpGet)) {
System.out.println(response1.getCode() + " " + response1.getReasonPhrase());
HttpEntity entity1 = response1.getEntity();
EntityUtils.consume(entity1);
}
//创建一个post类型的http请求
HttpPost httpPost = new HttpPost("http://www.baidu.com/");
List<NameValuePair> nvps = new ArrayList<>();
nvps.add(new BasicNameValuePair("username", "aaa"));
nvps.add(new BasicNameValuePair("password", "12345"));
//这是一个form表单类型的数据格式,放入request body中
httpPost.setEntity(new UrlEncodedFormEntity(nvps,Charsets.UTF_8));
//如果是json格式,如下处理,统一使用 UTF-8 可以避免 客户端与服务器端编码不一致问题
//httpPost.setEntity(new StringEntity("{}",Charsets.UTF_8));
try (CloseableHttpResponse response2 = httpclient.execute(httpPost)) {
//响应编码 以及 原因, 比如: 502 Bad Gateway
System.out.println(response2.getCode() + " " + response2.getReasonPhrase());
HttpEntity entity2 = response2.getEntity();
//将响应流转为字符串形式,你可以自己根据需要使用EntityUtils工具进行处理,当然也可以自己解析 InputStream 流
String stringBody = EntityUtils.toString(entity2, Charsets.UTF_8);
//简单打印下
System.out.println(stringBody);
//确保关闭流资源
EntityUtils.consume(entity2);
}
}
}
}


三、异步编程

/**
* @Description http client 异步编程
* @Author lvaolin
* @Date 2021/2/9 4:40 下午
*/
public class HttpClientAsync {
public static void main(String[] args) throws IOException, ExecutionException, InterruptedException {
//CloseableHttpAsyncClient的实例创建同样也有多种方式,高级用法就是用HttpAsyncClients.custom() 定制自己的参数
try (CloseableHttpAsyncClient httpclient = HttpAsyncClients.createDefault()) {
//这句话是异步编程api 独有的
httpclient.start();
//-----1、 下面是第一种用法--通过获取Future句柄来灵活控制逻辑---
//执行一个get请求
SimpleHttpRequest request10 = SimpleHttpRequests.get("http://www.baidu.com/");
SimpleHttpRequest request11 = SimpleHttpRequests.get("https://yonyou.com/");
//下面这2步是关键,非堵塞的,返回 future句柄
Future<SimpleHttpResponse> future10 = httpclient.execute(request10, null);
Future<SimpleHttpResponse> future11 = httpclient.execute(request11, null);
//等待响应返回
SimpleHttpResponse response10 = future10.get();
System.out.println(response10.getBodyText());
SimpleHttpResponse response11 = future11.get();
//-----2、 下面是第二种用法--通过FutureCallback 来实现理想中的异步回调 ---
//CountDownLatch 是否使用 取决于你的业务需要,
//你如果想在它回调完成时 再去做一些事情,就可以用 CountDownLatch来控制,否则就可以去掉CountDownLatch的代码
final CountDownLatch latch1 = new CountDownLatch(1);
final SimpleHttpRequest request2 = SimpleHttpRequests.get("http://www.baidu.com/");
httpclient.execute(request2, new FutureCallback<SimpleHttpResponse>() {
@Override
public void completed(SimpleHttpResponse response2) {
latch1.countDown();
System.out.println(request2.getRequestUri() + "->" + response2.getCode());
}
@Override
public void failed(Exception ex) {
latch1.countDown();
System.out.println(request2.getRequestUri() + "->" + ex);
}
@Override
public void cancelled() {
latch1.countDown();
System.out.println(request2.getRequestUri() + " cancelled");
}
});
latch1.await();
}
}
}


四、fluent风格编程

public class HttpClientFluent {
public static void main(String[] args) throws IOException {
//这种风格的编程确实很简洁清晰,给人流畅的感觉------
Content content = Request.get("http://www.baidu.com/")
.setHeader("Connection","keep-alive")
.execute()
.returnContent();
System.out.println(content.asString());
Content content2 = Request.post("http://www.baidu.com/")
.setHeader("Connection","keep-alive")
.setHeader("Accept", "application/json")
.setHeader("Content-Type", "application/json")//设置header信息
.bodyByteArray("{ }".getBytes(Charsets.UTF_8))//发送json格式的数据
.execute()
.returnContent();
System.out.println(content2.asString());
}
}


五、脱离了浏览器如何缓存http 静态资源信息

/**
* @Description HttpClientCache插件的用途
* @Author lvaolin
* @Date 2021/2/9 3:36 下午
*/
public class HttpClientCache {
public static void main(String[] args) throws IOException {
CacheConfig cacheConfig = CacheConfig.custom()
.setMaxCacheEntries(1000)
.setMaxObjectSize(8192)
.build();
RequestConfig requestConfig = RequestConfig.custom()
.setConnectTimeout(3, TimeUnit.SECONDS)
.setConnectionRequestTimeout(10, TimeUnit.SECONDS)
.build();
CloseableHttpClient cachingClient = CachingHttpClients.custom()
.setCacheConfig(cacheConfig)
.setDefaultRequestConfig(requestConfig)
.build();
HttpCacheContext context = HttpCacheContext.create();
HttpGet httpget = new HttpGet("http://www.baidu.com/");
for (int i = 0; i <3 ; i++) {
CloseableHttpResponse response = cachingClient.execute(httpget, context);
try {
CacheResponseStatus responseStatus = context.getCacheResponseStatus();
switch (responseStatus) {
case CACHE_HIT:
System.out.println("A response was generated from the cache with " +
"no requests sent upstream");
break;
case CACHE_MODULE_RESPONSE:
System.out.println("The response was generated directly by the " +
"caching module");
break;
case CACHE_MISS:
System.out.println("The response came from an upstream server");
break;
case VALIDATED:
System.out.println("The response was generated from the cache " +
"after validating the entry with the origin server");
break;
}
} finally {
response.close();
}
}
cachingClient.close(CloseMode.GRACEFUL);
}
}


六、总结

         在合适的场景使用异步编程能很好的提高程序的性能,打个比喻就是 你在蒸米饭的同时可以去炒菜,还可以同时去烧水,三件事情并行执行,花费的总时间就是最耗时的那个活,如果是堵塞式串行执行,那么花费总时间就是三件事情的时间之和,有没有运筹学的味道在里面。

        fluent风格编程确实清晰易懂简洁流畅,有兴趣的同学可以学习这种风格。


以上是关于HTTP请求异步编程解决堵塞之痛-Apache HttpClient5.0的主要内容,如果未能解决你的问题,请参考以下文章

javascript的异步编程解决方案收集

半同步半异步高性能网络编程

Java异步执行多个HTTP请求的例子(需要apache http类库)

解决并发编程之痛的良药--结构化并发编程

android中非堵塞socket通信

UNIX网络编程——网络I/O模型