|
@@ -0,0 +1,135 @@
|
|
|
|
|
+package space.anyi.httpClient;
|
|
|
|
|
+
|
|
|
|
|
+import java.io.IOException;
|
|
|
|
|
+import java.net.URI;
|
|
|
|
|
+import java.net.http.HttpClient;
|
|
|
|
|
+import java.net.http.HttpRequest;
|
|
|
|
|
+import java.net.http.HttpResponse;
|
|
|
|
|
+import java.time.Duration;
|
|
|
|
|
+import java.util.List;
|
|
|
|
|
+import java.util.concurrent.CompletableFuture;
|
|
|
|
|
+import java.util.concurrent.TimeUnit;
|
|
|
|
|
+import java.util.stream.Collectors;
|
|
|
|
|
+
|
|
|
|
|
+/**
|
|
|
|
|
+ * 同步与异步请求示例
|
|
|
|
|
+ *
|
|
|
|
|
+ * <p>HttpClient 提供了两种发送方式:</p>
|
|
|
|
|
+ * <ul>
|
|
|
|
|
+ * <li><b>同步 {@code send()}</b>:阻塞当前线程直到收到完整响应;
|
|
|
|
|
+ * 返回 {@link HttpResponse},需处理 IOException/InterruptedException。</li>
|
|
|
|
|
+ * <li><b>异步 {@code sendAsync()}</b>:立即返回
|
|
|
|
|
+ * {@link CompletableFuture}<HttpResponse>,请求在后台线程执行;
|
|
|
|
|
+ * 通过 thenApply/whenComplete 等回调或 join() 阻塞获取结果。</li>
|
|
|
|
|
+ * </ul>
|
|
|
|
|
+ */
|
|
|
|
|
+public class SyncAsyncExample {
|
|
|
|
|
+
|
|
|
|
|
+ /** 服务基地址常量 */
|
|
|
|
|
+ private static final String BASE_URL = "http://localhost:8080";
|
|
|
|
|
+
|
|
|
|
|
+ private final HttpClient httpClient = HttpClient.newBuilder()
|
|
|
|
|
+ // 设置连接超时,避免长时间挂起
|
|
|
|
|
+ .connectTimeout(Duration.ofSeconds(10))
|
|
|
|
|
+ .build();
|
|
|
|
|
+
|
|
|
|
|
+ /** 构造查询用户列表的 GET 请求 */
|
|
|
|
|
+ private HttpRequest buildGetRequest() {
|
|
|
|
|
+ return HttpRequest.newBuilder()
|
|
|
|
|
+ .uri(URI.create(BASE_URL + "/api/users"))
|
|
|
|
|
+ .GET()
|
|
|
|
|
+ .build();
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 同步发送:send() 会阻塞当前线程直到响应返回。
|
|
|
|
|
+ *
|
|
|
|
|
+ * @return 用户列表响应
|
|
|
|
|
+ */
|
|
|
|
|
+ public HttpResponse<String> sendSync() throws IOException, InterruptedException {
|
|
|
|
|
+ HttpRequest request = buildGetRequest();
|
|
|
|
|
+
|
|
|
|
|
+ // 阻塞等待服务器响应,期间当前线程无法做其他事情
|
|
|
|
|
+ HttpResponse<String> response =
|
|
|
|
|
+ httpClient.send(request, HttpResponse.BodyHandlers.ofString());
|
|
|
|
|
+
|
|
|
|
|
+ return response;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 异步发送:sendAsync() 立即返回,请求在内部线程池执行。
|
|
|
|
|
+ *
|
|
|
|
|
+ * <p>join() 获取结果时异常以 {@link java.util.concurrent.CompletionException}
|
|
|
|
|
+ * 形式抛出,无需声明 InterruptedException。</p>
|
|
|
|
|
+ *
|
|
|
|
|
+ * @return 用户列表响应
|
|
|
|
|
+ */
|
|
|
|
|
+ public HttpResponse<String> sendAsync() {
|
|
|
|
|
+ HttpRequest request = buildGetRequest();
|
|
|
|
|
+
|
|
|
|
|
+ // 立即返回 Future,不阻塞
|
|
|
|
|
+ CompletableFuture<HttpResponse<String>> future =
|
|
|
|
|
+ httpClient.sendAsync(request, HttpResponse.BodyHandlers.ofString());
|
|
|
|
|
+
|
|
|
|
|
+ // join() 阻塞直到异步任务完成并返回 HttpResponse
|
|
|
|
|
+ return future.join();
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 异步发送并链式处理:演示 CompletableFuture 的回调链。
|
|
|
|
|
+ *
|
|
|
|
|
+ * <p>不需要 join 阻塞,直接在回调(thenApply)里消费结果。</p>
|
|
|
|
|
+ *
|
|
|
|
|
+ * @return 状态码与响应体前 100 字符组成的描述字符串
|
|
|
|
|
+ */
|
|
|
|
|
+ public CompletableFuture<String> sendAsyncWithCallback() {
|
|
|
|
|
+ HttpRequest request = buildGetRequest();
|
|
|
|
|
+
|
|
|
|
|
+ // thenApply 在异步线程中处理响应,无需阻塞主线程
|
|
|
|
|
+ return httpClient.sendAsync(request, HttpResponse.BodyHandlers.ofString())
|
|
|
|
|
+ // 回调处理:把响应转成简洁描述
|
|
|
|
|
+ .thenApply(response -> "状态码=" + response.statusCode()
|
|
|
|
|
+ + ", body=" + abbreviate(response.body()));
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 并发发送多个异步请求:一次发起 N 个请求,全部完成后汇总。
|
|
|
|
|
+ *
|
|
|
|
|
+ * @param count 并发的请求数量
|
|
|
|
|
+ * @return 所有请求的状态码列表
|
|
|
|
|
+ */
|
|
|
|
|
+ public List<Integer> sendAsyncInParallel(int count) {
|
|
|
|
|
+ // 为每个请求启动一个异步任务
|
|
|
|
|
+ List<CompletableFuture<HttpResponse<String>>> futures =
|
|
|
|
|
+ java.util.stream.IntStream.range(0, count)
|
|
|
|
|
+ .mapToObj(i -> httpClient.sendAsync(
|
|
|
|
|
+ buildGetRequest(),
|
|
|
|
|
+ HttpResponse.BodyHandlers.ofString()))
|
|
|
|
|
+ .collect(Collectors.toList());
|
|
|
|
|
+
|
|
|
|
|
+ // allOf 等待所有任务完成,再逐个 join 取出状态码
|
|
|
|
|
+ CompletableFuture<Void> all = CompletableFuture.allOf(
|
|
|
|
|
+ futures.toArray(new CompletableFuture[0]));
|
|
|
|
|
+ all.join();
|
|
|
|
|
+
|
|
|
|
|
+ return futures.stream()
|
|
|
|
|
+ .map(f -> f.join().statusCode())
|
|
|
|
|
+ .collect(Collectors.toList());
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /** 截断长文本以便展示 */
|
|
|
|
|
+ private String abbreviate(String s) {
|
|
|
|
|
+ return s.length() > 100 ? s.substring(0, 100) + "..." : s;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 综合演示:并发发起 3 个异步请求并统计各自耗时。
|
|
|
|
|
+ */
|
|
|
|
|
+ public void demoParallelTiming() {
|
|
|
|
|
+ long start = System.nanoTime();
|
|
|
|
|
+ List<Integer> codes = sendAsyncInParallel(3);
|
|
|
|
|
+ long costMs = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - start);
|
|
|
|
|
+
|
|
|
|
|
+ System.out.println("并发 3 个请求,状态码: " + codes + ",总耗时(ms): " + costMs);
|
|
|
|
|
+ }
|
|
|
|
|
+}
|