同步(synchronous)和异步(asynchronous)通常用于描述在执行操作时是否需要等待某个操作完成,以及如何处理返回结果
此处再提两个相关但不同的概念,并发(concurrency)和并行(parallelism)
对于 Java 中发起 HTTP 请求而言,一样有同步异步之分。看本文剩余部分之前首先需要学习用 Java 发起 HTTP 请求,对应后文的 getResponseContent()
,意为请求后获取响应内容,并在此篇博客文章中有介绍
💬相关
博客文章《Java发起HTTP请求并解析JSON返回数据》
https://blog.csdn.net/weixin_42077074/article/details/128672130
为了更好描述同步与异步,以下给出一个示例场景,我想发起 10 次 HTTP 请求,每次请求都带有参数开始时间 startTime
和 endTime
,为 2023 年 1 月 1 日至 2023 年 1 月 10 日十天中每日零点和下一日的零点,如第一次请求的参数为 2023-01-01 00:00:00
和 2023-01-02 00:00:00
同步发请求无非就是直接调用或在 for
里调用 getResponseContent()
// 设置请求头
Map headers = new HashMap(){{// 设置接收内容类型put("Accept","application/json");// 设置发送内容类型put("Content-Type","application/json;charset=UTF-8");// 设置字符集put("charset", "UTF-8");// 设置访问者系统引擎版本、浏览器信息的字段信息,此处伪装成用户通过浏览器访问put("User-Agent", "Mozilla/4.0 (compatible; MSIE 5.0; Windows NT; DigExt)");
}};// 设置开始时间和结束时间
LocalDateTime startTime = LocalDateTime.of(2023, 1, 1, 0, 0, 0);
LocalDateTime endTime = LocalDateTime.of(2023, 1, 2, 0, 0, 0);// 创建一个数组来保存结果
String[] results = new String[10];for (int i = 0; i < 10; i++) {// 格式化开始时间和结束时间String formattedTime1 = startTime.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));String formattedTime2 = endTime.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));// 设置参数Map params = new HashMap(){{put("startTime", formattedTime1);put("endTime", formattedTime2);}};// 发送请求并获取响应results[i] = getResponseContent("http://www.example.com","GET", headers, params);// 更新开始时间和结束时间startTime = startTime.plusDays(i).plusSeconds(i);endTime = endTime.plusDays(i).plusSeconds(i);
}// 输出结果
for (int i = 0; i < 10; i++) {System.out.println(results[i]);
}
倘若请求量特别大,还按同步的方式进行,综合程序处理、网络延迟等因素,会非常地慢。
而对于异步而言,实现方式就比较多了,如 Java 11 中可以直接使用 java.net.http.HttpClient
类来创建异步 HTTP 客户端并使用 API,网上方法很多,此处不再赘述了
然而笔者处于兼容等各种考虑还是 Java8,Java8 并没有内置的异步 HTTP 客户端,要么调用第三方库来实现,如 Apache 的 HttpAsyncClient
库
org.apache.httpcomponents httpasyncclient 4.1.4
要么借助 Java 8 的标准库提供的基于 CompletableFuture
的异步编程来实现,笔者也是使用这个方案
注意, CompletableFuture
是并发而不是并行意义上的异步
// 创建 10 个异步任务
CompletableFuture[] futures = new CompletableFuture[10];
LocalDateTime startTime = LocalDateTime.of(2023, 1, 1, 0, 0, 0);
LocalDateTime endTime = LocalDateTime.of(2023, 1, 2, 0, 0, 0);for (int i = 0; i < 10; i++) {// 格式化开始时间和结束时间String formattedTime1 = startTime.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));String formattedTime2 = endTime.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));// 设置参数Map params = new HashMap(){{put("startTime", formattedTime1);put("endTime", formattedTime2);}};// 设置异步请求futures[i] = asyncHttpRequest("http://www.example.com","GET", headers, params);// 更新开始时间和结束时间startTime.plusDays(i).plusSeconds(i);endTime.plusDays(i).plusSeconds(i);
}// 等待异步任务完成,超时时间为5秒
try {CompletableFuture.allOf(futures).get(5, TimeUnit.SECONDS);
} catch (InterruptedException | ExecutionException e) {e.printStackTrace();
} catch (TimeoutException e) {System.out.println("请求超时");e.printStackTrace();return;
}// 输出每个异步任务的结果
for (int i = 0; i < 10; i++) {try {System.out.println(futures[i].get());} catch (InterruptedException | ExecutionException e) {e.printStackTrace();}
}
其中的异步请求函数 asyncHttpRequest()
public static CompletableFuture asyncHttpRequest(String strURL, String method, Map headers, Map params) {return CompletableFuture.supplyAsync(() -> {try {return getResponseContent(strURL, method, headers, params);} catch (Exception e) {e.printStackTrace();return null;}});
}