当前位置: 首页 > 科技观察

Java9新特性解读:HTTP2和REPL

时间:2023-03-21 12:11:07 科技观察

Java9的炒作将不再局限于模块化(modularity),Java9正在收集大量额外的功能模块,作为Java提交EnhancementProposals(JEP),并在OpenJDK(JavaSE的参考实现项目)中实现。在本文中,我们将重点关注Java9整个生命周期中可能对开发人员的工作和生活影响最大的一些JEP,包括新的HTTP/2支持和JShellREPL(read-evaluate-print-loop),它带来了一个交互式的基于shell的Java开发环境和一个探索性的开发API。HTTP/2HTTP/2标准是HTTP协议的最新版本。当前版本HTTP/1.1始于1999年,存在非常严重的问题,包括:标头阻塞在HTTP/1.1中,接收响应的顺序与发送请求的顺序相同。这意味着,例如,当查看包含许多小图像的大型HTML页面时,图像资源将不得不排在HTML页面资源之后,并且在浏览器完全加载HTML页面之前无法发送图像资源。这是“头部阻塞”,会导致许多潜在的页面呈现问题。在HTTP/2中,响应数据可以分块传输,甚至可以交叉传输,真正实现了请求和响应的多路复用。一个站点的连接限制在HTTP/1.1标准中是这样描述的:“一个单用户客户端不能与任何服务器保持超过2个连接”。这种限制,加上头对头阻塞的问题,严重限制了页面的性能。HTTP/2打破了这个限制,认为连接是持久的,只有在用户跳转或技术故障事件发生时,连接才会关闭。使用多路复用将有助于减少页面性能瓶颈。HTTP控制标头开销当前版本的HTTP使用简单的、基于文本的HTTP标头来控制通信。这样做的好处是非常简单易懂,调试起来就像通过连接输入一些文本指定端口一样简单。但是,使用基于文本的协议会使小响应数据包不成比例地膨胀。此外,大量HTTP响应几乎没有负载(例如,HEAD请求只是为了确定资源是否已更改)。对于实际上只包含***修改时间的响应,使用完全基于文本的标头(大约700字节,在HTTP1.1中,它们不能被压缩,尽管这很容易做到)是当前的HTTP标准,令人难以置信的浪费。另一个想法是对HTTP标头使用二进制编码。这种方法可以大大提高小请求的速度,同时使用非常少的网络带宽。这正是HTTP/2选择的方法,虽然本着协议的精神应该选择基于文本的协议来制定标准,但二进制文件的效率有令人信服的理由让我们这样做。HTTP/2带来的期待HTTP/2标准由IETFHTTP工作组创建,该工作组由来自Mozilla、Google、Microsoft、Apple等公司的代表和工程师组成,高级工程师来自领先的CDN公司AkamaiMark诺丁汉是主席。因此,HTTP/2是一个旨在优化大型、高流量网站的版本。它在实现简单、调试方便的基础上保证了性能和网络带宽消耗。该组织主席总结了HTTP/2的一些关键属性:相同的HTTPAPI更低的请求网络成本和服务器端友好的缓存推送思维革命更多的加密方式为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请求:HttpResponseresponse=HttpRequest.create(newURI("http://www.infoq.com")).body(noBody()).GET()。send();intresponseCode=response.responseCode();StringresponseBody=response.body(asString());System.out.println(responseBody);该API符合流畅风格/构建器模式(fluent/builder),与现有遗留系统,它对开发人员来说更现代、更舒适。虽然目前的代码库只支持HTTP/1.1,但新的API已经包含在内。这允许开发人员在最终确定对HTTP/2的支持时实验性地使用和验证新的API。相关代码已进入OpenJDK沙盒仓库,即将登陆JDK9主干。届时,新的API将开始自动内置到Oracle的二进制测试版中。对HTTP/2的支持现已可用,并将在未来几个月内完成。同时,您可以使用Mercurial导出源代码,并根据AdoptOpenJDK构建指南编译您导出的代码,以便您可以实验性地使用新的API。第一批完成的功能之一是当前版本无法处理的异步API。该函数允许通过sendAsync()方法将长时间运行的请求切换到VM管理的后台线程:HttpRequestreq=HttpRequest.create(newURI("http://www.infoq.com")).body(noBody()).GET();CompletableFutureaResp=req.sendAsync();Thread.sleep(10);if(!aResp.isDone()){aResp.cancel(true);System.out.println("Failedtoreplyquickly...");return;}HttpResponseresponse=aResp.get();相较于HTTP/1.1的实现,新的API给开发者带来了最大的方便,因为HTTP/1.1不提供关于已经响应的信息发送到服务器HTTP/2允许客户端向已被服务器处理过的请求发送取消命令。许多JShell语言为探索性开发提供了交互式环境。在某些情况下(特别是Clojure和其他Lisp方言),交互式环境占用了开发人员的大部分(如果不是全部)编码时间。其他语言如Scala或JRuby也大量使用了REPL。当然,Java之前也引入过Beanshell脚本语言,但是还没有完全标准化,而且近些年该项目一直处于闲置状态。在Java8中引入NashornJavascript实现(和jjsREPL)打开了更广泛地思考REPL并使交互式开发成为可能的大门。从JEP222开始,将现代REPL引入Java9的努力位于OpenJDK的Kulla项目中。库拉这个名字来源于古巴比伦神话中的建筑之神。该项目的主旨是提供尽可能接近的“完整Java”体验。本项目不引入新的非Java语义,禁用Java语言中对交互开发无用的语义(如上层访问控制修改或同步语义)。与所有REPL一样,JShell提供命令行而不是类似IDE的体验。语句和表达式可以在执行状态上下文中立即求值,而不必打包到一个类中。方法也是自由浮动的,不必属于特定的类。相反,JShell使用称为“snippets”的代码片段来提供上层执行环境。与HTTP/2API类似,JShell在一个单独的项目中开发,以免在快速开发期间影响主线构建的稳定性。JShell预计将在2015年8月合并到主干中。开发人员现在可以按照AdoptOpenJDK说明从头开始构建Kulla(源代码可从Mercurial获得)。对于一些动手实验,使用独立的实验罐可能是最简单的。这些jar是社区为不想从头开始构建的开发人员构建的。这些实验性jar可从AdoptOpenJDKCloudBees的CI构建实例中获得。要使用它们,您需要安装Java9beta(或OpenJDK9的构建版本)。然后下载jar文件,重命名为kulla.jar,在命令行输入:$java-jarkulla.jar|WelcometoJShell--Version0.610|Type/helpforhelp->这是REPL的标准界面,像往常一样,命令以单个字符开始并最终发出。JShell有一个相当完整(但仍在发展中)的帮助语法,可以通过以下命令轻松获得:->/helpTypeaJava语言表达式、语句或声明。或键入以下命令之一:/lor/list[all]--listthesourceyouhavetyped/seteditor--settheexternaleditorcommandtouse/eor/edit--editasourceentryreferencedbynameorid/dor/drop--deleteasourceentryreferencedbynameorid/sor/save[all|history]--savethesourceyouhavetyped/oor/open--openafileassourceinput/vor/vars--列出已声明的变量及其值/mor/methods--listthedeclaredmethodsandtheirsignatures/cor/classes--listthedeclaredclasses/xor/exit--exittheREPL/ror/reset--reseteverythingintheREPL/for/feedback--feedbackinformation:off,concise,normal,verbose,default,或?/por/prompt--toggledisplayofapprompt/cpor/classpath--addapathtotheclasspath/hor/history--historyofwhatyouhavetyped/setstart--readfileandsetasthenewstart-updefinitions/savestart<文件>--将默认启动定义保存到文件/?或/帮助--此帮助消息/!--重新运行最后一个片段/--重新运行前一个片段/---重新运行前一个片段支持的快捷方式包括:--显示当前文本Shift的可能完成---forcurrentmethodorconstructorinvocation,showasynopsisofthemethod/constructorJShell支持TAB键自动补全,所以我们可以很容易的找到println()或者其他我们想用的方法:->System.out.printprint(printf(println(传统表达式求值也很容易,但是Java的静态类型特性比其他动态类型语言要严格一些JShell会自动创建临时变量来保存表达式的值并确保它们留在上下文中以备后用:->3*(4+5)|表达式值为:27|赋值给int类型的临时变量$1->System.out.println($1);27我们还可以使用/list命令查看目前输入的所有源代码:->/list9:3*(4+5)10:System.out.println($1);使用/vars命令显示所有变量(包括明确定义的和临时的),以及它们的当前值:->Strings="Dydhda"|AddedvariablesoftypeStringwithinitialvalue"Dydhda"->/vars|int$1=27|Strings="Dydhda"除了支持简单的行代码,REPL允许非常简单地创建类和其他用户定义的类型。例如,可以像这样用一个短行创建一个类(注意需要左括号和右括号):->Strings="Dydhda"|AddedvariablesoftypeStringwithinitialvalue"Dydhda"->/vars|int$1=27|Strings="Dydhda“JShell代码非常简洁、自由浮动的特性意味着我们可以非常简单地使用REPL来演示Java语言的功能。例如,让我们看看著名的类型问题,Java数组的协方差问题:->Pet[]pets=newPet[1]|AddedvariablepetsoftypePet[]withinitialvalue[LPet;@2be94b0f->Cat[]cats=newCat[(#20:1)这样的特性使JShell成为一个很好的教学或研究工具,并且最接近ScalaREPL体验。使用/classpath开关,可以加载额外的jar,允许在REPL中直接使用交互式探索API。参与的主要IDE已经开始提供支持JDK9早期版本的构建——包括Netbeans和EclipseMars。据说IntelliJ14.1支持JDK9,但目前还不清楚将对新的模块化JDK扩展提供多少支持。到目前为止,这些IDE不支持HTTP/2和JShell,因为这些功能尚未登陆OpenJDK主线,但开发人员应该期待它们很快出现在标准JDKbeta版本中,并且随后会有IDE插件。这些API仍在开发中,项目负责人正在积极寻求最终用户的使用和参与。JDK9Outreach计划也在进行中,以鼓励开发人员在JDK9到达之前在JDK9上测试他们的代码和应用程??序。HTTP/2和JShell并不是唯一正在开发的新功能——其他正在开发的JDK9新功能,如JEP包括JDKJDK9的宣传计划也鼓励开发人员测试他们的代码并在JDK9上运行应用程序。新功能正在开发的不仅包括HTTP/2和JShell——作为JEP,JDK9正在开发的其他新特性包括:102进程API更新(ProcessAPIUpdates)165编译器控制(CompilerControl)227Unicode7.0245ValidateJVMCommand-LineFlag参数248:使G1成为默认垃圾收集器一系列TLS更新(JEP219、244、249)可以在此处找到当前正在考虑的所有JEP(以及它们应该放置在哪个版本的Java中)的完整列表。