JEP 110:HTTP/2 客户端(孵化器)
概述
定义一个新的 HTTP 客户端 API,该 API 实现了 HTTP/2 和 WebSocket,并能够取代旧的 HttpURLConnection
API。该 API 将作为孵化模块提供,如 JEP 11 中所定义,随 JDK 9 一起发布。这意味着:
- 该 API 和实现将不会成为 Java SE 的一部分。
- 该 API 将位于
jdk.incubtor
命名空间下。 - 在编译或运行时,默认情况下该模块将无法解析。
动机
现有的 HttpURLConnection
API 及其实现存在诸多问题:
- 基础的
URLConnection
API 设计时考虑了多种协议,但其中几乎所有的协议现在都已经废弃(如ftp
、gopher
等)。 - 该 API 早于 HTTP/1.1,并且过于抽象。
- 它难以使用,且有许多未记录的行为。
- 它仅支持阻塞模式(即每个请求/响应需要一个线程)。
- 非常难以维护。
目标
- 必须易于在常见情况下使用,包括简单的阻塞模式。
- 必须提供诸如“头部已接收”、错误和“响应体已接收”等事件的通知。这种通知不一定基于回调,而是可以使用像
CompletableFuture
这样的异步机制。 - 一个简单且简洁的 API,满足 80%-90% 的应用程序需求。这可能意味着一个相对较小的 API 足迹,并不一定暴露协议的所有功能。
- 必须向服务器公开 HTTP 协议请求的所有相关方面,以及来自服务器的响应(头部、主体、状态码等)。
- 必须支持标准和常见的认证机制。最初将仅限于基本认证(Basic authentication)。
- 必须能够轻松设置 WebSocket 握手。
- 必须支持 HTTP/2。(HTTP/2 的应用层语义与 1.1 大致相同,尽管线路协议完全不同。)
- 必须能够在从 1.1 升级到 2(或不升级)时进行协商,或者从一开始就选择 2。
- 必须支持服务器推送,即服务器能够在没有客户端明确请求的情况下向客户端推送资源。
- 必须执行与现有网络 API 一致的安全检查。
- 应该对新的语言特性(如 lambda 表达式)友好。
- 应该对嵌入式系统需求友好,特别是避免永久运行的计时器线程。
- 必须支持 HTTPS/TLS。
- HTTP/1.1 的性能要求:
- 性能必须与现有的
HttpURLConnection
实现相当。 - 性能必须与 Apache HttpClient 库及作为客户端 API 使用的 Netty 和 Jetty 相当。
- 新 API 的内存消耗必须与作为客户端 API 使用的
HttpURLConnection
、Apache HttpClient、Netty 和 Jetty 相当或更低。
- 性能必须与现有的
- HTTP/2 的性能要求:
- 在新协议预期的方式上(即可扩展性和延迟),性能必须优于 HTTP/1.1,但需考虑任何平台限制(例如 TCP 段确认窗口)。
- 作为 HTTP/2 客户端 API 使用时,性能必须与 Netty 和 Jetty 相当。
- 新 API 的内存消耗必须与作为客户端 API 使用的
HttpURLConnection
、Apache HttpClient、Netty 和 Jetty 相当或更低。
- 性能比较只会在可比的操作模式下进行,因为新 API 将强调简单性和易用性,而不是覆盖所有可能的用例。
- 此工作计划用于 JDK 9。其中一些代码可能会被 Java EE 在其 Servlet 4.0 API 中实现 HTTP/2 时重用,因此只会使用 JDK 8 的语言特性和尽可能多的 API。
- 预计通过在 JDK 9 中使用 API 的经验,可以在 JDK 10 中将 API 标准化到 Java SE 的 java.net 命名空间中。届时,作为未来 JEP 的一部分,API 将不再以孵化器模块的形式存在。
非目标
该 API 最终旨在取代新代码中的 HttpURLConnection
API,但我们并不打算立即使用新 API 重新实现旧 API。这可能会作为未来的工作进行。
在 JDK 8 的这个 JEP 的早期版本中考虑了一些需求,但为了使 API 尽可能简单,这些需求被排除在外:
- 请求/响应过滤,
- 可插拔的连接缓存,以及
- 通用的升级机制。
其中一些需求(例如连接缓存)会随着 HTTP/2 的逐渐普及而变得不那么重要。
描述
一些原型设计工作已经在 JDK 9 中完成,其中为 HTTP 客户端、请求和响应分别定义了不同的类。构建器模式被用于将可变实体与不可变产物分离。定义了用于发送和接收的同步阻塞模式,并且还定义了基于 java.util.concurrent.CompletableFuture
的异步模式。
该原型基于 NIO SocketChannels 构建,其异步行为通过 Selectors 实现,并由外部提供的 ExecutorServices 支持。
原型实现是独立的,即现有的协议栈未被改变,以确保兼容性,并允许采用阶段性方法,即并非所有功能都必须在一开始就得到支持。
原型 API 还包括:
- 分离请求和响应,类似于 Servlet 和 HTTP 服务器 API;
- 异步通知以下事件:
- 响应头已接收,
- 响应错误,
- 响应体已接收,以及
- 服务器推送(仅限 HTTP/2);
- 通过
SSLEngine
实现 HTTPS; - 代理;
- Cookie;以及
- 身份验证。
API 中最可能需要进一步工作的部分是在支持 HTTP/2 多响应(服务器推送)和 HTTP/2 配置方面。原型实现几乎支持所有的 HTTP/1.1,但还不支持 HTTP/2。
将在接下来的变更中实现 HTTP/2 代理功能。
替代方案
目前已经存在许多 HTTP 客户端 API 及其实现,例如 Jetty 和 Apache HttpClient。然而,这两者在包和类的数量上都显得较为庞大,并且没有利用诸如 lambda 表达式之类的新语言特性。
测试
内部 HTTP 服务器将为回归测试和 TCK 测试提供一个合适的基础。功能测试也可以使用它,但可能需要针对真实的 HTTP 服务器进行测试。