HTTP 准则

本文档介绍了 Google API 如何与不同的 HTTP 版本和实现结合使用。如果您使用的是我们生成或专门编写的 客户端库(我们建议的 方法适用于大多数用例),您无需担心这些细节。这些库会自动为您处理与服务器的低层级通信。

如果您是一位经验丰富的开发者,并且需要编写自己的自定义代码以使用第三方 HTTP 客户端访问 API 的 REST 接口,您应该了解此处记录的相关概念以及您选择的 HTTP 库提供的功能。

使用传输协议 (HTTP/*)

本部分介绍 Google API 可用于在客户端和服务器之间进行通信的支持的传输协议(通常是某个版本的 HTTP),以及我们为您建议的协议使用方式。

HTTP 语义

开发 API 客户端代码时,请遵循标准 HTTP 协议 语义。服务器端代理或 API 堆栈可能仅支持标准 HTTP 功能的子集,并且还可能支持其向后兼容的版本。

需要由 API 的服务器端实现处理的 HTTP 协议语义由服务器堆栈控制。仅当这些功能明确记录为 API 规范(例如缓存支持)的一部分时,才能依赖此类语义。

HTTP 版本

客户端可以使用客户端平台或其客户端网络允许或者与服务器端代理协商的任何 HTTP/* 协议。支持的协议包括 HTTP/1.1、HTTP/2 和 HTTP/3 (QUIC)。我们强烈建议不要使用 HTTP/1.0 的旧版支持。

某些 API 功能可能只有较新版本的 HTTP 协议才支持;部分功能仅使用 HTTP/2 和 HTTP/3(例如全双工多路复用流式传输)完全指定。如果您需要将这些功能中的任何功能作为 API 规范的一部分,请注意不同 HTTP 版本的限制。请注意,HTTP/2 服务器推送等旧功能已被弃用,并且不受现代 Web 客户端支持。

通常,为了提高性能、减少队头阻塞和提高网络故障的恢复能力,我们建议使用 HTTP/3 或 HTTP/2。

渠道

渠道是指第 4 层网络连接,对于 HTTP/1.1 和 HTTP/2,通常是 TCP 套接字,对于 HTTP/3 (QUIC),则是 UDP 套接字。客户端应用不应假定渠道的管理方式是端到端的,因为连接几乎总是由 Google Front End (GFE) 代理代表服务器进程终止。

HTTP/1.1 客户端:如果使用 HTTP/1.1,应始终重复使用 TCP 连接 (Connection: Keep-Alive)。HTTP 客户端库通常会管理连接池,以方便重复使用。避免在 HTTP/1.1 连接上使用 HTTP 流水线;它支持不佳,可能会导致问题。如需了解详情,请参阅 HTTP 和 TCP。

HTTP/2 和 HTTP/3 客户端:现代客户端和浏览器主要使用 HTTP/2 或 HTTP/3。这两种协议都支持多路复用,允许通过单个连接同时进行多个请求和响应。

  • HTTP/2 - 每个来源使用单个 TCP 连接。
  • HTTP/3 - 每个来源使用单个 QUIC 连接(通过 UDP)。QUIC 集成了 TLS 加密、拥塞控制和连接管理,通常具有连接设置速度更快(0-RTT 或 1-RTT)以及不受跨流的 TCP 队头阻塞影响等优势。

使用 HTTP/2 和 HTTP/3 时,浏览器对单个主机的并行 TCP 连接数(例如 2-10)的限制不再是主要的性能问题。 但是,请注意,服务器(或 GFE 等代理)仍然可以对单个 HTTP/2 或 HTTP/3 连接中的并发流数量施加限制。这样可以防止过载并确保公平使用资源(例如,将每个连接的并发请求或流限制为 100)。

HTTPS

客户端可以通过 API 规范支持的 HTTPS 或 HTTP 访问 API。TLS 协商和 TLS 版本对客户端应用是透明的。默认情况下,Google API 仅接受 HTTPS 流量。

请求和响应格式

本部分介绍了 API 交互的结构,包括使用网址编码的数据、用于 RESTful 操作的特定 HTTP 方法以及基于 JSON 的载荷格式。

请求网址

JSON-REST 映射支持网址编码形式的请求数据,HTTP 请求和响应正文使用 application/json 作为 Content-Type。

HTTP 正文使用 JSON 数组来支持流式 RPC 方法,JSON 数组可能包含任意数量的 JSON 消息或错误状态 JSON 消息。

长请求网址

网址具有实际长度限制,默认情况下通常设置为 16KB,但可能会因服务器而异。如果您的 API 使用网址超过此长度的 GET 请求,则请求可能无法到达目标 API 服务器,并且会被 Google Front End (GFE) 拒绝,并显示错误消息 Your client has issued a malformed or illegal request.

要避开此限制,客户端代码应使用 Content-Type 为 application/x-www-form-urlencoded 且 HTTP 标头为 X-HTTP-Method-Override: GET 的 POST 请求。该方法也适用于 DELETE 请求。

HTTP 方法(动词)

如果请求网址符合 REST 模型,则将其 HTTP 方法指定为 API 规范的一部分。特别是,每种 API 方法必须 符合基于 API 方法映射到的特定 HTTP 动词的 HTTP 协议的要求。如需了解详情,请参阅 超文本传输协议 规范和 PATCH 方法 RFC。

安全方法(如 HTTP GET 和 HEAD)不应表示检索以外的操作。具体来说,HTTP GET 应该安全,不应对客户端产生任何显而易见的副作用。

HTTP 中的幂等性意味着多个相同请求产生的副作用与单个请求相同。GET、PUT 和 DELETE 是与风格指南相关的幂等 HTTP 方法。请注意,幂等性仅通过服务器副作用的形式表示,并未指定有关响应的任何内容。特别是对于不存在的资源,DELETE 应该 返回 404 (Not Found)。

HTTP POST 和 PATCH 既不安全也不具有幂等性。(PATCH 在 RFC 5789 中引入)

HTTP 动词 安全 幂等
GET 是 是
PUT   是
DELETE   是
POST  
PATCH  

载荷格式

  • 请求和响应应共享相同的 Content-Type,除非请求是带有 application/x-www-form-urlencoded 正文的 GET 或 POST。

  • JSON 在 application/json MIME 类型中受支持。从 proto3 到 JSON 的映射在 JSON Mapping 中正式指定。

  • 根据用于将请求字段映射到查询参数的相同 REST 样式映射规则,可以使用表单参数 (POST) 代替网址查询参数 (GET)。支持的 Content-Type 为 application/x-www-form-urlencoded。

流式

本部分详细介绍了 Google API 如何处理客户端和服务器流式传输,具体说明了半双工与全双工通信的限制以及流式 JSON 消息的编码要求。

半双工与全双工

HTTP 是一种请求响应协议,允许通过面向信息流的不同传输协议(如 TCP (HTTP/1.x))或其多路复用变体 (SPDY、HTTP/2、QUIC) 传递其请求或响应主体。

作为客户端开发者,您的应用可以采用流式传输模式生成请求正文,即客户端流式传输。同样地,应用也可以采用流式传输模式使用响应正文,即服务器流式传输。

但是,HTTP 规范未指定在请求正文仍处于挂起状态时是否允许服务器流式传输回响应正文(错误响应除外)。这种语义称为全双工流式传输。 尽管许多 HTTP 客户端/服务器/代理软件确实支持全双工流式传输,即使 HTTP/1.1 也支持,但为了避免出现任何互操作性问题,基于 HTTP 的 Cloud API 仅限于半双工流式传输。

默认情况下,Cloud API 中的出价 Bidi 流式传输方法会采用全双工语义。 也就是说,使用 HTTP 调用这样的方法是不安全的。如果流式传输方法仅为半双工(由服务器强制执行),则 API 文档应明确指定半双工行为。

对于浏览器客户端,标准 HTTP 语义进一步受浏览器网络 API 的限制。浏览器使用 XHR 或 Fetch 支持服务器流式传输(通常遵循传输级别的帧处理方式)。Fetch API 使用 whatwg 流。

由于浏览器的限制,需要浏览器支持的 Cloud API 必须避免采用客户端流式传输以及全双工流式传输,或者为浏览器客户端提供单独的 API。

一般来说,通过互联网进行客户端流式传输不如服务器流式传输有用。这是因为使用客户端流式传输通常会导致有状态服务,这会对负载平衡造成负面影响,让系统更容易出现故障或受到攻击。另一方面,服务器流式传输可能很有用,因为它可以显着减少有长时间 RTT 延迟的网络上的延迟时间。

消息编码

JSON 消息在流式传输时将作为 JSON 消息数组进行编码。请求或响应正文将作为有效的 JSON MIME 类型保留。

客户端流式传输编码示例:

1 <length> <message-bytes> 1 <length> <message-bytes>  … EOF

服务器流式传输编码示例:

1 <length> <message-bytes>  … 2 <length> <status-bytes> EOF

线级编码:StreamBody 的定义仅在其 为字段“messages”和“status” <length> 分配 tag-id 时才有意义,而“status”对于正常消息将采用 1-2 字节的 varint 编码,因此总的编码 开销是每条消息 2-3 个字节。

需要添加可选的填充字段,才能支持使用 base64 编码的流:

message StreamBody {
  repeated bytes message = 1;
  google.rpc.Status status = 2;
  repeated bytes padding = 15;   // max one-byte tag-id: xxx01111
}

错误消息应作为 JSON 或 protobuf 数组的最后一个元素附加,其格式与常规消息相同。

状态管理

适用于客户端或服务器的任何 HTTP 版本中都很好地定义了半关闭行为,旨在向另一端发送正文已完成的信号。

特别是,客户端代码在等待响应时可以自由完成请求。与之类似,当请求正文仍在写入服务器时,客户端可能会看到完成的响应。HTTP 标准要求客户端在以意外方式完成响应时(通常具有错误状态)中止或完成请求。 这就是说,在正常情况下,服务器不应在客户端仍在发送请求时完成响应。

取消

通过取消支持,客户端能够在请求或响应仍在等待处理时中止请求。

HTTP/1.* 客户端没有可靠的取消支持,因为客户端可在请求完成后随时关闭 TCP 连接,而不会中止请求或响应事务。采用 HTTP/1.1 的 TCP FIN 不应被解释为取消,即使连接被标记为保持 keep-alive (Connection: Keep-Alive) 也是如此。

但在客户端关闭 TCP 连接后,如果服务器尝试将任何数据写入客户端,则会生成 RST,这会触发取消。

另请注意,取消也是非流式传输 API 存在的问题。当响应涉及长轮询并且因此导致连接长时间保持空闲时,此问题尤其明显。

SPDY、HTTP/2 和 QUIC 支持显式取消,尤其是与 go-away 消息结合使用。

Keep-alive

通过 Keep-alive 支持,客户端或服务器能够检测到失败的对等体,即使在出现丢包或网络故障的情况下也是如此。

HTTP/1.1 不提供 keep-alive 支持,因为 TCP keep-alive 不是一种可行的方法。

QUIC 或 HTTP/2 提供特殊控制消息,旨在实现由应用(包括浏览器)提供的 keep-alive 支持。

但是,实现可靠的 keep-alive 和故障检测将可能需要有一个客户端库,加上必要的服务器端支持:如果依赖将基本 HTTP 作为通信协议,通过互联网进行长期流式传输通常容易出错。

流控制

流控制支持要求客户端将传输级流控制事件传播到客户端应用。实际的机制取决于客户端应用所使用的 HTTP 客户端 API 执行机制。例如,为了防止客户端或服务器过载,您需要使用显式流控制支持阻塞写入和读取或者非阻塞读取和写入,以便应用处理和遵循流控制事件。

HTTP/1.1 依赖于 TCP 流控制。

SPDY 和 HTTP/2 在流级别具有自己的流控制,由于请求通过单个 TCP 连接进行多路复用,因此它们在连接级别进一步受到 TCP 流控制的限制。

QUIC 在 UDP 上运行,因此可以完全自行管理流控制。