跳到主要内容

JEP 110:HTTP/2 客户端(孵化器)

QWen Max 中英对照

概述

定义一个新的 HTTP 客户端 API,该 API 实现了 HTTP/2 和 WebSocket,并能够取代旧的 HttpURLConnection API。该 API 将作为孵化模块提供,如 JEP 11 中所定义,随 JDK 9 一起发布。这意味着:

  • 该 API 和实现将不会成为 Java SE 的一部分。
  • 该 API 将位于 jdk.incubtor 命名空间下。
  • 在编译或运行时,默认情况下该模块将无法解析。

动机

现有的 HttpURLConnection API 及其实现存在诸多问题:

  • 基础的 URLConnection API 设计时考虑了多种协议,但其中几乎所有的协议现在都已经废弃(如 ftpgopher 等)。
  • 该 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 及其实现,例如 JettyApache HttpClient。然而,这两者在包和类的数量上都显得较为庞大,并且没有利用诸如 lambda 表达式之类的新语言特性。

测试

内部 HTTP 服务器将为回归测试和 TCK 测试提供一个合适的基础。功能测试也可以使用它,但可能需要针对真实的 HTTP 服务器进行测试。