Java9新功能之HTTP2和REPL

2015 年 8 月 07 日

对 Java 9 的炒作将不再局限于模块化(modularity),Java 9 正在搜罗大量额外的功能模块,这些功能模块正作为 Java 增强提案(JEP)提交,并在 OpenJDK (Java SE 的参考实现项目)中实现。

在这篇文章中,我们将重点关注一些或将在 Java 9 整个生命周期中,对开发者的工作生活影响最大的 JEP,包括新的 HTTP/2 支持和 JShell REPL(读取 - 求值 - 打印 - 循环),后者带来了基于 shell 的交互式 Java 开发环境和探索性开发 API。

HTTP/2

HTTP/2 标准是 HTTP 协议的最新版本。当前版本 HTTP/1.1 始于 1999 年,存在着非常严重的问题,包括:

对头阻塞

在 HTTP/1.1 中,响应接收的顺序和请求发送的顺序相同。这意味着,例如,当查看一个包含许多小图像的大 HTML 页面时,图像资源将不得不在 HTML 页面资源之后排队,在浏览器完全加载完 HTML 页面之前,图像资源无法被发送。这就是“对头阻塞”,会导致许多潜在的页面渲染问题。

在 HTTP/2 中,响应数据可以按块(chunk)传输,甚至可以交叉传输,因此真正实现了请求和响应的多路复用。

一个站点的连接数限制

在 HTTP/1.1 标准中有这样的描述:“一个单用户的客户端不能与任何服务器保持 2 个以上的连接”。这个限制和对头阻塞问题一起,严重限制了页面的性能。

HTTP/2 打破这种限制并认为连接是持久的,只有当用户跳转后或者发生技术性故障事件时,连接才会关闭。对多路复用的使用将有助于降低页面性能瓶颈。

HTTP 控制头的开销

当前的 HTTP 版本使用简单的、基于文本的 HTTP 头信息来控制通信。这样做的优点是非常简单且易于理解,调试也很简单,只需通过连接指定端口并输入一些文本。然而,使用基于文本的协议会让小的响应包不成比例地膨胀。此外,大量的 HTTP 响应几乎没有或者根本没有有效负载(比如,HEAD 请求只是要确定资源是否发生变化)。为实际上只包含最后修改时间的响应,使用完全基于文本的头信息(大约有 700 个字节,在 HTTP1.1 中,它们不能被压缩,尽管很容易做到)是当前 HTTP 标准中,不可思议的浪费。

另一个思路是对 HTTP 头信息使用二进制编码。这种方式能够极大地提高较小请求的速度且占用的网络带宽非常小。这正是 HTTP/2 已经选择的方法,虽然以协议精神制定标准应该选择基于文本的协议,但是二进制的效率有令人信服的理由,让我们这样做。

HTTP/2 带来的期望

HTTP/2 标准是由 IETF HTTP 工作组创建的,该组织由来自 Mozilla、Google、 Microsoft、Apple,以及其他公司的代表和工程师组成,由来自 CDN 领军公司 Akamai 的高级工程师 Mark Nottingham 任主席。因此,HTTP/2 是一个为优化大型、高流量的网站而生的版本,它在实现简单、易于调试的基础上,确保了性能和网络带宽消耗。

该组织主席总结了一些 HTTP/2 的关键属性

  • 相同的 HTTP API
  • 成本更低的请求
  • 网络和服务器端友好
  • 缓存推送
  • 思维革命
  • 更多加密方式

带给 Java 的意义

自从 1.0 版本开始,Java 就支持 HTTP,但是多数代码出自完全不同的时代。例如,Java 对 HTTP 的支持是围绕相对协议无关的框架(URL 类)设计的,因此在网站成为主导地位的 90 年代,这种实现显得很不清晰。

Java 对 HTTP 的支持是基于当时最好的设计思想,但是时过境迁,最重要的是 Java 对 HTTP 原始的支持出来时,HTTPS 还没有出现。因此,Java 的 API 将 HTTPS 作为一种移花接木,导致了不能简化的复杂性。

在现代社会,HTTPS 开始变得无所不在,让 HTTP 日渐成为落后的技术。甚至,美国政府现在都通过了完全迁到 HTTPS-only 的计划。

JDK 内核对 HTTP 的支持已经无法跟上现实网络的发展步伐。实际上,甚至 JDK8 也只不过是交付了一个支持 HTTP/1.0 的客户端,然而,大多数的开发者早已转而使用第三方客户端库了,比如 Apache 的 HttpComponents。

所有这一切意味着,对 HTTP/2 的支持将是 Java 未来十年的核心功能。这也让我们重新审视我们的固有思维,重新写一套 API 并提供重新来过的机会。HTTP/2 将是未来数年内,每位开发者主要面对的 API。

新的 API 不再坚持协议中立性,使开发者可以完全抛弃过去的使用方式。这套 API 只关注 HTTP 协议,但是要进一步理解的是 HTTP/2 并没有从根本上改变原有的语义。因此,这套 API 是 HTTP 协议独立的,同时提供了对新协议中帧和连接处理的支持。

在新的 API 中,一个简单的 HTTP 请求,可以这样创建和处理:

复制代码
HttpResponse response = HttpRequest
.create(new URI("http://www.infoq.com"))
.body(noBody())
.GET().send();
int responseCode = response.responseCode();
String responseBody = response.body(asString());
System.out.println(responseBody);

这种符合流畅风格 / 建造者模式(fluent/builder)的 API,与现存的遗留系统相比,对开发者来说,更具现代感和舒适感。

虽然当前的代码库只支持 HTTP/1.1,但是已经包含了新的 API。这使得在对 HTTP/2 支持完成对过程中,开发者可以实验性地使用和验证新的 API。

相关代码已经进入 OpenJDK 沙箱仓库中,并很快登陆 JDK 9 的主干。到那个时候,新的 API 将开始自动构建到 Oracle 的二进制 beta 版本中。现在,对 HTTP/2 的支持已经可用,并将在未来数月内最终完成。

在此期间,你可以使用 Mercurial 迁出源代码,并根据 AdoptOpenJDK 构建指导编译你迁出地代码,这样你就可以实验性地使用新的 API 了。

第一批完成的功能之一是当前版本力不能及的异步 API。这个功能让长期运行的请求,可以通过 sendAsync() 方法,切换到 VM 管理的后台线程中:

复制代码
HttpRequest req = HttpRequest
.create(new URI("http://www.infoq.com"))
.body(noBody())
.GET();
CompletableFuture<HttpResponse> aResp = req.sendAsync();
Thread.sleep(10);
if (!aResp.isDone()) {
aResp.cancel(true);
System.out.println("Failed to reply quickly...");
return;
}
HttpResponse response = aResp.get();

相比 HTTP/1.1 的实现,新的 API 带给开发者最多的是方便性,因为 HTTP/1.1 没有提供对已经发送到服务器端的请求的取消机制,而 HTTP/2 可以让客户端向已经被服务器端处理的请求,发送取消命令。

JShell

很多语言都为探索性开发提供了交互式环境。在某些情况下(特别是 Clojure 和其他 Lisp 方言),交互式环境占据了开发者的大部分编码时间,甚至是全部。其他语言,比如 Scala 或者 JRuby 也广泛使用 REPL。

当然,此前 Java 曾经推出过 Beanshell 脚本语言,但是它没有实现完全标准化,而且近年来,该项目已经处于不活跃状态。在 Java 8(以及 jjs REPL)中引入的 Nashorn Javascript 实现打开了更广泛地考虑 REPL 并将交互式开发成为可能的大门。

一项努力将现代 REPL 引入 Java 9 的工作,以 JEP 222 作为开始,收录在 OpenJDK 的 Kulla 项目中。Kulla 这个名字来自古巴比伦神话,是建造之神。该项目的主旨是提供最近距离的“完整 Java”体验。该项目没有引入新的非 Java 语义,并禁用了 Java 语言中对交互式开发没有用处的语义(比如上层的访问控制修改或同步的语义)。

与所有 REPL 一样,JShell 提供了命令行,而不是类似 IDE 的体验。语句和表达式能够在执行状态上下文中,被立即求值,而不是非得打包到类中。方法也是自由浮动的,而不必属于某个特定的类。相反,JShell 使用代码片断“snippets”来提供上层执行环境。

与 HTTP/2 API 相似,JShell 已经在独立的项目开发,以免在快速发展的时期影响主干构建的稳定性。JShell 预计在 2015 年 8 月期间合并到主干。

现在,开发者可以参考 AdoptOpenJDK 说明指导,从头构建 Kulla(源代码可以从 Mercurial 地址获得)。

对于一些上手实验,最简单的可能是使用一个独立的试验 jar。这些 jar 包是社区专为不想从头构建的开发者构建好的。

这些试验 jar 包可以从 AdoptOpenJDK CloudBees 的 CI 构建实例中获得。

要使用它们,你需要安装 Java 9 beta 版(或者 OpenJDK 9 的构建版本)。然后下载 jar 文件,重命名为 kulla.jar,然后在命令行输入如下:

复制代码
$ java -jar kulla.jar
| Welcome to JShell -- Version 0.610
| Type /help for help
->

这是 REPL 的标准界面,和往常一样,命令是从单个字符开始并最终发出的。

JShell 有一个相当完整(但仍在发展)的帮助语法,可以通过如下命令轻松获得:

复制代码
-> /help
Type a Java language expression, statement, or declaration.
Or type one of the following commands:
/l or /list [all] -- list the source you have typed
/seteditor <executable> -- set the external editor command to use
/e or /edit <name or id> -- edit a source entry referenced by name or id
/d or /drop <name or id> -- delete a source entry referenced by name or id
/s or /save [all|history] <file> -- save the source you have typed
/o or /open <file> -- open a file as source input
/v or /vars -- list the declared variables and their values
/m or /methods -- list the declared methods and their signatures
/c or /classes -- list the declared classes
/x or /exit -- exit the REPL
/r or /reset -- reset everything in the REPL
/f or /feedback <level> -- feedback information: off,
concise, normal, verbose, default, or ?
/p or /prompt -- toggle display of a prompt
/cp or /classpath <path> -- add a path to the classpath
/h or /history -- history of what you have typed
/setstart <file> -- read file and set as the new start-up definitions
/savestart <file> -- save the default start-up definitions to the file
/? or /help -- this help message
/! -- re-run last snippet
/<n> -- re-run n-th snippet
/-<n> -- re-run n-th previous snippet
Supported shortcuts include:
-- show possible completions for the current text
Shift- -- for current method or constructor invocation,
show a synopsis of the method/constructor

JShell 支持 TAB 键自动补全, 因此我们可以很容易找到 println() 或者其他我们想使用的方法:

复制代码
-> System.out.print
print( printf( println(

传统的表达式求值也很容易,但是相比其他动态类型语言,Java 的静态类型特征会更严格一点。JShell 会自动创建临时变量来保存表达式的值,并确保它们保持在上下文域内供以后使用:

复制代码
-> 3 * (4 + 5)
| Expression value is: 27
| assigned to temporary variable $1 of type int
-> System.out.println($1);
27

我们还可以使用 /list 命令,查看到目前为止输入的所有源代码:

复制代码
-> /list
9 : 3 * (4 + 5)
10 : System.out.println($1);

使用 /vars 命令显示所有的变量(包括显式定义的和临时的),以及他们当前持有的值:

复制代码
-> String s = "Dydh da"
| Added variable s of type String with initial value "Dydh da"
-> /vars
| int $1 = 27
| String s = "Dydh da"

除了支持简单的代码行,REPL 还允许非常简单地创建类和其它用户定义的类型。例如,可以用如下短短一行来创建类(请注意,开始和结束括号是必需的):

复制代码
-> class Pet {}
| Added class Pet
-> class Cat extends Pet {}
| Added class Cat

JShell 代码非常简洁、自由浮动的性质意味着我们可以非常简单地使用 REPL 来演示 Java 语言的功能。例如,让我们来看看著名的类型问题,即 Java 数组的协变问题:

复制代码
-> Pet[] pets = new Pet[1]
| Added variable pets of type Pet[] with initial value [LPet;@2be94b0f
{1}
-> Cat[] cats = new Cat[1]
| Added variable cats of type Cat[] with initial value [LCat;@3ac42916
{1}
-> pets = cats
| Variable pets has been assigned the value [LCat;@3ac42916
{1}
-> pets[0] = new Pet()
| java.lang.ArrayStoreException thrown: REPL.$REPL13$Pet
| at (#20:1)

这样的功能使 JShell 成为一种伟大的教学或研究工具,而且最接近 Scala REPL 的体验。使用 /classpath 切换,可以加载额外的 jar 包,从而可以在 REPL 直接使用互动式探索性 API。

参与

主要的 IDE 已开始提供支持 JDK 9 早期版本的构建——包括 Netbeans Eclipse Mars IntelliJ 14.1 据称支持 JDK9,但目前还不清楚对新的模块化 JDK 扩展的支持力度。

到目前为止,这些 IDE 还不支持 HTTP/2 和 JShell,因为这些功能还没有登陆 OpenJDK 的主干,但是开发者应该很期望它们能够早日出现在标准的 JDK beta 版本中,并且有 IDE 插件可以紧随其后。这些 API 仍在开发中,项目的领导者正在积极寻求最终用户的使用和参与。

The JDK 9 Outreach programme is also underway to encourage developers to test their code and applications on JDK 9 before it arrives. HTTP/2 & JShell aren’t the only new features being worked on - other new JDK 9 functionality under development as JEPs includes

JDK 9 的宣传计划也正在鼓励开发者测试他们的代码并在 JDK 9 上运行应用程序。正在开发的新功能不止包括 HTTP/2 和 JShell—— 其他作为 JEP,JDK 9 正在开发的新功能还包括:

  • 102 Process API 的更新(Process API Updates)
  • 165 编译器控制(Compiler Control)
  • 227 Unicode 7.0
  • 245 验证虚拟机代码行标记参数(Validate JVM Command-Line Flag Arguments)
  • 248: G1 作为默认的垃圾回收器(Make G1 the Default Garbage Collector)
  • TLS 的一系列更新(TLS Updates) (JEP 219, 244, 249)

目前正在审议(以及考虑应该放在哪个 Java 版本)的所有 JEP 的完整列表可以在这里找到。

关于作者

Ben Evans是 Java/JVM 性能分析初创公司 jClarity 的 CEO。在业余时间,他是伦敦 Java 社区的领导者之一并且是 Java 社区进程执行委员会的一员。他之前的项目经验包括 Google IPO 的性能测试、金融交易系统、为 90 年代一些最大的电影编写备受好评的网站等。

查看英文原文: Java 9’s New HTTP/2 and REPL

2015 年 8 月 07 日 00:469547

评论

发布
暂无评论
发现更多内容

linux入门系列8--shell编程入门

黑马腾云

Linux centos Shell linux命令 linux编程

【Kafka】消费者客户端小结(java)

guoguo 👻

第11周总结

娄江国

企业信息化怎么构建?

代码制造者

大数据 低代码 企业信息化 零代码 编程开发

INT类型知多少

Simon

MySQL

第11周作业

娄江国

全票通过!易观开源项目DolphinScheduler进入Apache孵化器

易观大数据

内容审核平台助力猫爪构建健康安全的社交环境

百度大脑

人工智能 百度 百度大脑 内容审核

37岁程序员被裁,想用6月工资跪舔领导划掉被裁名额,结果蒙了!

程序员生活志

​JDK1.8新特性(八):还在重复写空指针检查代码?赶紧使用Optional吧!​

xcbeyond

Java 新特性 JDK1.8 Optional

英特尔神经拟态芯片Loihi大显身手 帮助轮椅上的儿童实现独立生活

飞天鱼2017

可能是首个支持部署 Deno 前后端应用的部署工具

binggg

taro GitHub 前端 deno Node

学习笔记

Qx

学习

35岁大厂程序员被劝退!老板说:没年轻人有冲劲!真有内味了吗?

程序员生活志

程序员 职场

王者荣耀为什么不使用微服务架构?

程序员生活志

一位男程序员的英语学习之路

盛安德软件

物联网SIM卡和SIM卡真的不是一回事

华为云开发者社区

人工智能 物联网 华为云 传感器 SIM卡

终极学习法,你能学会任何东西--程序员的学习之路

盛安德软件

linux入门系列7--管道符、重定向、环境变量

黑马腾云

Linux centos 运维 linux命令 管道符

提高GIT中代码质量的七点优秀实践

程序员生活志

git 经验总结

linux入门系列6--软件管理之rpm和yum仓库

黑马腾云

Linux centos 运维 rpm yum

区块链技术正向平台化、组件化、集成化演进

CECBC区块链专委会

大数据 区块链技术 科技

要老婆吗? AR一键生成的那种

程序员生活志

非IT行业大企程序员讲述MIS系统开发案例

Learun

敏捷开发 企业信息化 企业管理 .net core 「Java 25周年」

开源,轻松实现RTC与SIP互通

anyRTC开发者

WebRTC 编码 SIP 源码解析

linux入门系列9--用户管理及文件权限控制

黑马腾云

Linux centos centos7 linux运维 linux用户权限

Devops与敏捷二者能否结合?

DevOps Scrum 敏捷开发

linux入门系列10--firewalld防火墙管理

黑马腾云

Linux centos 防火墙 linux运维 linux防火墙

区块链技术助力甘肃建食安信息追溯平台 为食品安全“立规矩”

CECBC区块链专委会

食品追溯 食品安全

网页游戏

小端taro

CHAR与VARCHAR详解

Simon

MySQL

Java9新功能之HTTP2和REPL-InfoQ