跳到主要内容

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

概括

定义一个新的 HTTP 客户端 API,该 API 实现 HTTP/2 和 WebSocket,并且可以替换旧HttpURLConnectionAPI。该 API 将作为孵化器模块提供,如JEP 11中所定义,与 JDK 9 一起提供。这意味着:

  • API 和实现不会成为 Java SE 的一部分。

  • 该 API 将位于 jdk.incubtor 命名空间下。

  • 默认情况下,该模块不会在编译或运行时解析。

动机

现有的HttpURLConnectionAPI及其实现存在很多问题:

  • 基本URLConnectionAPI 在设计时考虑了多种协议,几乎所有协议现在都已失效(ftpgopher等)。

  • 该 API 早于 HTTP/1.1,并且过于抽象。

  • 它很难使用,有许多未记录的行为。

  • 它仅在阻塞模式下工作(即每个请求/响应一个线程)。

  • 维护起来非常困难。

目标

  • 必须易于在常见情况下使用,包括简单的阻止模式。

  • 必须提供事件通知,例如“收到标头”、错误和“收到响应正文”。此通知不一定基于回调,而是可以使用像 CompletableFuture 这样的异步机制。

  • 简单简洁的API,可满足80-90%的应用需求。这可能意味着 API 占用空间相对较小,不一定会暴露协议的所有功能。

  • 必须向服务器公开 HTTP 协议请求的所有相关方面以及服务器的响应(标头、正文、状态代码等)。

  • 必须支持标准和通用的身份验证机制。这最初仅限于基本身份验证。

  • 必须能够轻松设置 WebSocket 握手。

  • 必须支持HTTP/2。 (HTTP/2 的应用程序级语义与 1.1 基本相同,尽管有线协议完全不同。)

    • 必须能够协商从 1.1 升级到 2(或不能),或者从一开始就选择 2。

    • 必须支持服务器推送,即服务器能够在无需客户端显式请求的情况下将资源推送给客户端。

  • 必须执行与现有网络 API 一致的安全检查。

  • 应该对新的语言功能(例如 lambda 表达式)友好。

  • 应适合嵌入式系统要求,特别是避免永久运行的计时器线程。

  • 必须支持 HTTPS/TLS。

  • HTTP/1.1 的性能要求:

    • 性能必须与现有HttpURLConnection实施相媲美。

    • 当用作客户端 API 时,性能必须与 Apache HttpClient 库以及 Netty 和 Jetty 相当。

    • HttpURLConnection当用作客户端 API 时,新 API 的内存消耗必须等于或低于Apache HttpClient、Netty 和 Jetty。

  • HTTP/2 的性能要求:

    • 尽管有任何平台限制(例如,TCP 段确认窗口),但在新协议所期望的方面(即可扩展性和延迟),性能必须优于 HTTP/1.1。

    • 当用作 HTTP/2 的客户端 API 时,性能必须与 Netty 和 Jetty 相当。

    • HttpURLConnection当用作客户端 API 时,新 API 的内存消耗必须与使用 、Apache HttpClient 以及 Netty 和 Jetty时的内存消耗相当或更低。

  • 性能比较只会在可比较的操作模式下进行,因为新的 API 将强调简单性和易用性,而不是涵盖所有可能的用例,

  • 这项工作适用于 JDK 9。Java EE 在 Servlet 4.0 API 中实现 HTTP/2 时可能会重复使用某些代码,因此将仅使用 JDK 8 语言功能以及 API(如果可能)。

  • 目的是凭借在 JDK 9 中使用 API 的经验,可以在 JDK 10 中的 java.net 命名空间下标准化 Java SE 中的 API。当这种情况发生时,作为未来 JEP 的一部分,API将不再作为孵化器模块存在。

非目标

该 API 的目的是最终替换HttpURLConnection新代码的 API,但我们并不打算立即使用新 API 重新实现旧 API。这可能会在未来的工作中发生。

JDK 8 的 JEP 的早期版本考虑了一些要求,但为了使 API 尽可能简单,这些要求被省略:

  • 请求/响应过滤,
  • 可插拔连接缓存,以及
  • 通用升级机制。

随着 HTTP/2 的逐渐采用,其中一些要求(例如连接缓存)将变得不那么重要。

描述

JDK 9 已经完成了一些原型设计工作,其中为 HTTP 客户端、请求和响应定义了单独的类。构建器模式用于将可变实体与不可变产品分开。为发送和接收定义了同步阻塞模式,还定义了基于 java.util.concurrent.CompletableFuture 的异步模式。

该原型构建在 NIO SocketChannel 上,并通过 Selectors 和外部提供的 ExecutorServices 实现异步行为。

原型实现是独立的,即现有堆栈没有更改,以确保兼容性并允许采用分阶段方法,其中并非所有功能必须在开始时得到支持。

原型 API 还包括:

  • 分离请求和响应,如 Servlet 和 HTTP 服务器 API;
  • 以下事件的异步通知:
    • 收到响应标头,
    • 响应错误,
    • 收到响应正文,并且
    • 服务器推送(仅限 HTTP/2);
  • HTTPS,通过SSLEngine;
  • 代理;
  • 饼干;和
  • 验证。

API 最可能需要进一步工作的部分是支持 HTTP/2 多响应(服务器推送)和 HTTP/2 配置。原型实现支持几乎所有 HTTP/1.1,但尚不支持 HTTP/2。

HTTP/2 代理将在以下更改中实现。

备择方案

存在许多现有的 HTTP 客户端 API 和实现,例如JettyApache HttpClient。就包和类的数量而言,这两者都相当重量级,并且它们没有利用新的语言功能,例如 lambda 表达式。

测试

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