如何使用Java 11 HTTP Client API实现HTTP/2服务器推送

发布时间:2021-11-20 17:02:53 作者:小新
来源:亿速云 阅读:132

这篇文章将为大家详细讲解有关如何使用Java 11 HTTP Client API实现HTTP/2服务器推送,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。

对 HttpUrlConnection 你还有印象吗?JDK 11为 HttpUrlConnection 重新设计了 HTTP Client API。HTTP Client API 使用简单,支持 HTTP/2(默认)和 HTTP/1.1。为了向后兼容,当服务器不支持 HTTP/2时,HTTP Client API 会自动从 HTTP/2 降到 HTTP1.1。 

此外,HTTP Client API 支持同步和异步编程模型,并依靠 stream 传输数据(reactive stream)。它还支持 WebSocket 协议,用于实时 Web 应用程序,降低客户端与服务器间通信开销。

除了多路复用(Multiplexing),HTTP/2 另一个强大的功能是服务器推送。传统方法(HTTP/1.1)中,主要通过浏览器发起请求 HTML 页面,解析接收的标记(Markup)并标识引用的资源(例如JS、CSS、图像等)。 

为了获取资源,浏览器会继续发送资源请求(每个资源一个请求)。相反,HTTP/2 会发送 HTML 页面和引用的资源,不需要浏览器主动请求。因此,浏览器请求 HTML 页面后,就能收到页面以及显示所需的所有其他信息。HTTP Client API 通过 PushPromiseHandler 接口支持 HTTP/2 功能。 

接口实现必须作为 send() 或 sendAsync() 方法的第三个参数填入。PushPromiseHandler 依赖下面三项协同:

调用特定 acceptor 函数接受 push promise。acceptor 函数必须传入一个 BodyHandler(不能为 null)用来处理 Promise 的 request body。acceptor 函数会返回一个 CompletableFuture 实例,完成 promise response。

基于以上信息,看一下 PushPromiseHandler 实现:

private static final List<CompletableFuture<Void>>
  asyncPushRequests = new CopyOnWriteArrayList<>();
...
private static HttpResponse.PushPromiseHandler<String> pushPromiseHandler() {
  return (HttpRequest initiatingRequest,
     HttpRequest pushPromiseRequest,
     Function<HttpResponse.BodyHandler<String> ,
     CompletableFuture<HttpResponse<String>>> acceptor) -> {
     CompletableFuture<Void> pushcf =
        acceptor.apply(HttpResponse.BodyHandlers.ofString())
        .thenApply(HttpResponse::body)
        .thenAccept((b) -> System.out.println(
           "\nPushed resource body:\n " + b));
        asyncPushRequests.add(pushcf);
        System.out.println("\nJust got promise push number: " +
           asyncPushRequests.size());
        System.out.println("\nInitial push request: " +
           initiatingRequest.uri());
        System.out.println("Initial push headers: " +
           initiatingRequest.headers());
        System.out.println("Promise push request: " +
           pushPromiseRequest.uri());
        System.out.println("Promise push headers: " +
           pushPromiseRequest.headers());
  };
}

现在,触发一个 request 把 PushPromiseHandler 传给 sendAsync():

HttpClient client = HttpClient.newHttpClient();
HttpRequest request = HttpRequest.newBuilder()
  .uri(URI.create("https://http2.golang.org/serverpush"))
  .build();
client.sendAsync(request,
  HttpResponse.BodyHandlers.ofString(), pushPromiseHandler())
     .thenApply(HttpResponse::body)
     .thenAccept((b) -> System.out.println("\nMain resource:\n" + b))
     .join();
asyncPushRequests.forEach(CompletableFuture::join);
System.out.println("\nFetched a total of " +
  asyncPushRequests.size() + " push requests");

完整源代码可在 GitHub 上找到。

github.com/PacktPublishing/Java-Coding-Problems/tree/master/Chapter13/P268_ServerPush

如果要把所有 push promise 及 response 汇总到指定的 map 中,可以使用 PushPromiseHandler.of() 方法,如下所示:

privatestatic final ConcurrentMap<HttpRequest,
  CompletableFuture<HttpResponse<String>>> promisesMap
     = new ConcurrentHashMap<>();
privatestatic final Function<HttpRequest,
  HttpResponse.BodyHandler<String>> promiseHandler
     = (HttpRequest req) -> HttpResponse.BodyHandlers.ofString();
public static void main(String[] args)
        throws IOException, InterruptedException {
  HttpClient client = HttpClient.newHttpClient();
  HttpRequest request = HttpRequest.newBuilder()
     .uri(URI.create("https://http2.golang.org/serverpush"))
     .build();
  client.sendAsync(request,
     HttpResponse.BodyHandlers.ofString(), pushPromiseHandler())
        .thenApply(HttpResponse::body)
        .thenAccept((b) -> System.out.println("\nMain resource:\n" + b))
        .join();function(){   //外汇跟单www.gendan5.com   System.out.println("\nPush promises map size: " +
     promisesMap.size() + "\n");
  promisesMap.entrySet().forEach((entry) -> {
     System.out.println("Request = " + entry.getKey() +
         ", \nResponse = " + entry.getValue().join().body());
  });
}
privatestatic HttpResponse.PushPromiseHandler<String> pushPromiseHandler() {
  return HttpResponse.PushPromiseHandler.of(promiseHandler, promisesMap);
}

完整源代码可在 GitHub 上找到。

github.com/PacktPublishing/Java-Coding-Problems/tree/master/Chapter13/P268_ServerPushToMap

前面两个解决方案中 BodyHandler 都用到了 String 类型的 ofString()。如果服务器还需要推送二进制数据(比如图像),就不是很适用。因此,如果要处理二进制数据,则需要用 ofByteArray() 切换到byte[] 类型的 BodyHandler。也可以用 ofFile() 把 push 资源保存到磁盘,下面的解决方案是之前方案的改进版:

private static final ConcurrentMap<HttpRequest,
  CompletableFuture<HttpResponse<Path>>>
     promisesMap = new ConcurrentHashMap<>();
private static final Function<HttpRequest,
  HttpResponse.BodyHandler<Path>> promiseHandler
     = (HttpRequest req) -> HttpResponse.BodyHandlers.ofFile(
       Paths.get(req.uri().getPath()).getFileName());
public static void main(String[] args)
                 throws IOException, InterruptedException {
  HttpClient client = HttpClient.newHttpClient();
  HttpRequest request = HttpRequest.newBuilder()
     .uri(URI.create("https://http2.golang.org/serverpush"))
     .build();
  client.sendAsync(request, HttpResponse.BodyHandlers.ofFile(
     Path.of("index.html")), pushPromiseHandler())
        .thenApply(HttpResponse::body)
        .thenAccept((b) -> System.out.println("\nMain resource:\n" + b))
        .join();
  System.out.println("\nPush promises map size: " +
     promisesMap.size() + "\n");
  promisesMap.entrySet().forEach((entry) -> {
     System.out.println("Request = " + entry.getKey() +
        ", \nResponse = " + entry.getValue().join().body());
  });
}
private static HttpResponse.PushPromiseHandler<Path> pushPromiseHandler() {
  return HttpResponse.PushPromiseHandler.of(promiseHandler, promisesMap);
}

关于“如何使用Java 11 HTTP Client API实现HTTP/2服务器推送”这篇文章就分享到这里了,希望以上内容可以对大家有一定的帮助,使各位可以学到更多知识,如果觉得文章不错,请把它分享出去让更多的人看到。

推荐阅读:
  1. cat /etc/glance/glance-api.conf
  2. docker python api 安装配置的详解

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

api java http

上一篇:Java软件安装小技巧是怎样的

下一篇:怎么搭建Mysql单机实例

相关阅读

您好,登录后才能下订单哦!

密码登录
登录注册
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》