千家信息网

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

发表于:2024-11-25 作者:千家信息网编辑
千家信息网最后更新 2024年11月25日,这篇文章将为大家详细讲解有关如何使用Java 11 HTTP Client API实现HTTP/2服务器推送,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。对 Htt
千家信息网最后更新 2024年11月25日如何使用Java 11 HTTP Client API实现HTTP/2服务器推送

这篇文章将为大家详细讲解有关如何使用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 依赖下面三项协同:

  • 客户端发起的 send request(initiatingRequest)

  • 合成 push request(pushPromiseRequest)

  • acceptor 函数,必须成功调用该函数才能接受 push promise(acceptor)

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

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

private static final List>
asyncPushRequests = new CopyOnWriteArrayList<>();
...
private static HttpResponse.PushPromiseHandler pushPromiseHandler() {
return (HttpRequest initiatingRequest,
HttpRequest pushPromiseRequest,
Function ,
CompletableFuture>> acceptor) -> {
CompletableFuture 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   CompletableFuture>> promisesMap
= new ConcurrentHashMap<>();
privatestatic final Function HttpResponse.BodyHandler> 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 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   CompletableFuture>>
promisesMap = new ConcurrentHashMap<>();
private static final Function HttpResponse.BodyHandler> 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 pushPromiseHandler() {
return HttpResponse.PushPromiseHandler.of(promiseHandler, promisesMap);
}

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

服务器 服务 资源 函数 支持 推送 浏览器 页面 浏览 数据 方案 方法 篇文章 二进制 信息 功能 可在 图像 客户 客户端 数据库的安全要保护哪些东西 数据库安全各自的含义是什么 生产安全数据库录入 数据库的安全性及管理 数据库安全策略包含哪些 海淀数据库安全审计系统 建立农村房屋安全信息数据库 易用的数据库客户端支持安全管理 连接数据库失败ssl安全错误 数据库的锁怎样保障安全 type—c的数据库 软件定制和软件开发区别 无锡华硕服务器维修服务站 如果让程序服务器开机启动 服务器配置与管理第2版 百度如何连上搜索服务器 sql链接到服务器 服务器名称 上饶网络安全宣传 单细胞数据库 参加网络技术部的演讲 网络安全发展论坛 普陀区网络技术服务参考价格 几何图形设计软件开发 机器人应用软件开发商 dns的服务器地址怎么恢复 从软件开发模型变化的方式 江西网络安全事件应急预案 上位机软件开发vb下载 网络安全知识安全教案 拨打电话显示服务器已暂停 2020级数据库考试试题 哪个开源数据库好用 身体综合走势图数据库中彩网 华为网络安全证书过期 网络安全自查情况报告幼儿园 安全错误服务器证书无效 网络安全伴我行绘画图片 锐科网络技术有限公司怎么样 网络技术ip地址块聚合 金山区品质软件开发专业服务
0