Java9新特性对HTTP2协议支持与非阻塞HTTP API


Posted in Java/Android onMarch 16, 2022

Java9新特性对HTTP2协议支持与非阻塞HTTP API

在HTTP/1.1 发布了16 年之后,IETF在2015年终于通过了HTTP/2 协议。HTTP/2协议旨在降低延迟,满足当今时代对于信息响应时间的要求。在这篇文章中,我会简要的对HTTP/2协议进行介绍,然后我们将重点放在研究Java9中对HTTP/2支持及其HTTP客户端API的变化。

一、HTTP/2简介

HTTP/2 旨在减轻 HTTP/1.1 维护复杂基础结构所造成的痛苦,性能良好。尽管 HTTP/2 仍然与 HTTP/1.1 向后兼容,但它不再是基于文本的协议。

HTTP/2 多路复用使单个连接可以处理多个双向流,允许客户端通过单个连接同时下载多个资源。

HTTP 1.x 协议是基于文本的,因此报文很冗长。有的时候,同一组 HTTP Headers被一遍又一遍地交换。HTTP/2 通过跨请求维护 HTTP Headers,消除重复交换的数据,大大减少了数据交互所需的带宽。

HTTP/2数据推送

您可能认为HTTP/2的服务端数据推送是对 WebSockets 的某种延续或升级,但情况并非如此。虽然 WebSockets 是客户端和服务器之间全双工通信的一种方法,以便服务器在建立 TCP 连接后将数据发送到客户端,但 HTTP/2 提供了一种不同的解决方案。

HTTP/2 推送是主动向客户端发送资源,而无需从客户端的角度发起资源请求。这意味着服务器端根据一个请求可能知道网站进一步需要的其他资源,并且早在客户端再次发起请求它们之前,就可以一并(提前)发送所有资源。

目前支持 HTTP/2 的 Java HTTP 客户端

  • Jetty
  • Netty
  • OkHttp
  • Vert.x
  • Firefly

但是在这篇文章中,我们不会介绍这些Java 客户端软件,而是介绍Java9提供的HTTP/2支持。

二、Java 9 的 HTTP/2 客户端

首先使用Java 9的语法进行模块的导入 。jdk.incubator.httpclient

module com.springui.echo.client {
    requires jdk.incubator.httpclient;
}

Java 9 新的 HTTP Cient API 遵循构建器模式。HttpClient是用来操作HTTP请求的入口点,先构建后使用。

HttpClient client = HttpClient
    .newBuilder()
    .version(Version.HTTP_2)  //支持HTTP2
    .build();

在阻塞模式下发送请求

一旦我们有了一个 HttpClient实例,就可以用它来发送HttpRequest,HttpRequest实例也可以使用构造器创建。

HttpResponse<String> response = client.send(
    HttpRequest
        .newBuilder(TEST_URI)  //请求地址
        .POST(BodyProcessor.fromString("Hello world")) //POST报文数据
        .build(),
    BodyHandler.asString()  //请求响应数据处理,接收字符串
);

请求发出去之后,线程会一直阻塞直到得到响应数据,这个和JAVA 8及之前的HTTP API是一样的。但是Java 9提供了以异步非阻塞发送处理请求的方法,更适合高并发的HTTP请求与处理。

以非阻塞模式发送请求(Java 9)

在下面的示例中,10 个随机整数以异步方式发送请求。

List<CompletableFuture<String>> responseFutures = 
        IntStream.of(1,2,3,4,5,6,7,8,9,10)  //10个整数形成IntStream,Java 8的语法
        .mapToObj(String::valueOf) //10个整数转换成字符串,Java 8的语法
        .map(message -> client.sendAsync( //将10个整数字符串作为内容,发送10个异步请求
                HttpRequest.newBuilder(TEST_URI)
                        .POST(HttpRequest.BodyProcessor.fromString(message))
                        .build(),
                HttpResponse.BodyHandler.asString()
             ).thenApply(HttpResponse::body)  //以CompletableFuture<HttpResponse.body()>作为流处理的返回值
        )
        .collect(Collectors.toList());  //将Stream转成List

上面的例子大量的使用了Java 8的Stream流式处理的API,如果不熟悉的同学,可以翻看我以前写的一些文章。

sendAsync方法的返回值CompletableFuture<HttpResponse<String>>,使用thenApply(HttpResponse::body) 作了进一步的处理,最终返回值是CompletableFuture<String>。

CompletableFuture是Java异步编程的知识,将并发的异步处理结果打印出来。

responseFutures.stream().forEach(future -> {
    LOGGER.info("Async response: " + future.getNow(null));
});

你会注意到,最终的打印日志可能不是按照顺序1、2、3、4、5、6、7、8、9、10进行处理的,因为所有的请求都是异步发送出去的,返回的结果是CompletableFuture用于异步处理的结果。

三、支持HTTP2的Push-Promise Frames

以上所有示例在 HTTP/1.1协议下都可以完成,只是新加了非阻塞的异步API,也没涉及到 HTTP/2 特性啊。别急,Java 9 Client API与HTTP/2结合最紧密的就是:可以使用HTTP2发送一个请求,得到多个异步数据结果。(某些数据提前推送,当然这需要服务端也支持HTTP/2进行配合)

Map<HttpRequest,CompletableFuture<HttpResponse<String>>> responses =
        client.sendAsync(    //注意这里只发送一次请求
          HttpRequest.newBuilder(TEST_URI)
                  .POST(HttpRequest.BodyProcessor.fromString(TEST_MESSAGE))
                  .build(),
          HttpResponse.MultiProcessor.asMap(    //多个资源的响应结果
                  request -> Optional.of(HttpResponse.BodyHandler.asString())
          )
).join();
responses.forEach((request, responseFuture) -> {
  LOGGER.info("Async response: " + responseFuture.getNow(null));
});

从Java 9的角度来看,新的HTTP/2客户端API看起来不错。但笔者觉得目前相关技术的使用还不是很成熟,我觉得大家暂时尝尝鲜就可以。

以上就是Java9新特性对HTTP2协议的支持与非阻塞HTTP API的详细内容,更多关于支持HTTP2协议及非阻塞HTTP API的资料请关注三水点靠木其它相关文章!

Java/Android 相关文章推荐
Java常用函数式接口总结
Jun 29 Java/Android
mybatis 获取无数据的字段不显示的问题
Jul 15 Java/Android
Java面试题冲刺第十九天--数据库(4)
Aug 07 Java/Android
JPA 通过Specification如何实现复杂查询
Nov 23 Java/Android
深入浅出讲解Java8函数式编程
Jan 18 Java/Android
关于ObjectUtils.isEmpty() 和 null 的区别
Feb 28 Java/Android
Java线程的6种状态与生命周期
May 11 Java/Android
springboot读取nacos配置文件
May 20 Java/Android
Ubuntu18.04下QT开发Android无法连接设备问题解决实现
Jun 01 Java/Android
Java中的Kotlin 内部类原理
Jun 16 Java/Android
详解Spring Security如何在权限中使用通配符
Jun 28 Java/Android
OpenFeign实现远程调用
Aug 14 Java/Android
Java练习之潜艇小游戏的实现
Mar 16 #Java/Android
你知道Java Spring的两种事务吗
Java并发编程之原子性-Atomic的使用
Java9新特性之Module模块化编程示例演绎
Mar 16 #Java/Android
JVM的类加载器和双亲委派模式你了解吗
Java生成日期时间存入Mysql数据库的实现方法
Mar 03 #Java/Android
Java设计模式之享元模式示例详解
You might like
打造计数器DIY三步曲(上)
2006/10/09 PHP
php异常处理使用示例
2014/02/25 PHP
PHP时间类完整实例(非常实用)
2015/12/25 PHP
PHP7新增运算符用法实例分析
2016/09/26 PHP
php 二维数组快速排序算法的实现代码
2017/10/17 PHP
laravel框架查询数据集转为数组的两种方法
2019/10/10 PHP
用JavaScript仿PS里的羽化效果代码
2011/12/20 Javascript
jQuery Trim去除字符串首尾空字符的实现方法说明
2014/02/11 Javascript
iframe实用操作锦集
2014/04/22 Javascript
推荐25个超炫的jQuery网格插件
2014/11/28 Javascript
JQuery中属性过滤选择器用法实例分析
2015/05/18 Javascript
js+html5通过canvas指定开始和结束点绘制线条的方法
2015/06/05 Javascript
[原创]Javascript 实现广告后加载 可加载百度谷歌联盟广告
2016/05/11 Javascript
jQuery Easyui使用(二)之可折叠面板动态加载无效果的解决方法
2016/08/17 Javascript
AngularJS实现的获取焦点及失去焦点时的表单验证功能示例
2017/10/25 Javascript
webpack4.x打包过程详解
2018/07/18 Javascript
vue 使某个组件不被 keep-alive 缓存的方法
2018/09/21 Javascript
vue中的过滤器及其时间格式化问题
2020/04/09 Javascript
vue 中的动态传参和query传参操作
2020/11/09 Javascript
[42:32]完美世界DOTA2联赛循环赛 Magma vs PXG BO2第二场 10.28
2020/10/28 DOTA
python简单实现刷新智联简历
2016/03/30 Python
Python二叉树定义与遍历方法实例分析
2018/05/25 Python
python函数装饰器之带参数的函数和带参数的装饰器用法示例
2019/11/06 Python
关于Pytorch的MNIST数据集的预处理详解
2020/01/10 Python
matlab灰度图像调整及imadjust函数的用法详解
2020/02/27 Python
python3:excel操作之读取数据并返回字典 + 写入的案例
2020/09/01 Python
css3翻牌翻数字的示例代码
2020/02/07 HTML / CSS
美国名表在线商城:Ashford(支持中文)
2019/09/24 全球购物
以太网Ethernet IEEE802.3
2013/08/05 面试题
电子商务毕业生求职信
2013/11/10 职场文书
法律专业推荐信范文
2013/11/29 职场文书
求职信内容怎么写
2014/05/26 职场文书
大学生干部培训心得体会
2016/01/06 职场文书
党员反四风学习心得体会
2016/01/22 职场文书
手把手带你彻底卸载MySQL数据库
2022/06/14 MySQL
Java代码规范与质量检测插件SonarLint的使用
2022/08/05 Java/Android