请求的处理方式

区域 ID

REGION_ID 是 Google 根据您在创建应用时选择的区域分配的缩写代码。此代码不对应于国家/地区或省,尽管某些区域 ID 可能类似于常用国家/地区代码和省代码。对于 2020 年 2 月以后创建的应用,REGION_ID.r 包含在 App Engine 网址中。对于在此日期之前创建的现有应用,网址中的区域 ID 是可选的。

详细了解区域 ID

本文档介绍 App Engine 应用如何接收请求和发送响应。如需了解详情,请参阅请求标头和响应参考文档

如果您的应用使用服务,您可以将请求发送到特定服务或此服务的特定版本。如需详细了解服务可寻址性,请参阅请求的路由方式

处理请求

应用负责启动 Web 服务器和处理请求。您可以使用支持您的开发语言的任何 Web 框架。

当 App Engine 收到针对您应用的 Web 请求时,会调用与网址对应的 servlet,如应用的 WEB-INF/ 目录中的 web.xml 文件中所述。App Engine 支持 Java Servlet 2.5 或 3.1 API 规范,以便将请求数据提供给 servlet 并接受响应数据。

App Engine 运行应用的多个实例,每个实例都有各自用于处理请求的 Web 服务器。任何请求都可以路由到任何实例,因此来自同一用户的连续请求并不一定会发送到同一实例。实例的数量会随着流量的变化而自动调整。

以下示例 servlet 类在用户浏览器上显示简单消息。

// With @WebServlet annotation the webapp/WEB-INF/web.xml is no longer required.
@WebServlet(name = "requests", description = "Requests: Trivial request", urlPatterns = "/requests")
public class RequestsServlet extends HttpServlet {

  @Override
  public void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
    resp.setContentType("text/plain");
    resp.getWriter().println("Hello, world");
  }
}

配额和限制

App Engine 会随着流量的增加自动为您的应用分配资源。但是,这一过程会受到下列限制:

  • App Engine 会为低延时应用(应用响应请求的时间少于 1 秒)预留自动扩缩容量。

  • 严重受 CPU 限制的应用还可能发生额外的延时,以便与同一服务器上的其他应用有效地共享资源。静态文件请求不受这些延迟限制的影响。

发送到应用的所有传入请求都会计入请求限额。为响应请求而发送的数据会计入传出带宽(计费)限额。

HTTP 和 HTTPS(安全)请求均计入请求传入带宽(计费)传出带宽(计费)限额。Google Cloud 控制台的“配额详情”页面还会分别列出安全请求次数安全传入带宽安全传出带宽的值,以供参考。仅 HTTPS 请求计入这些值。如需了解详情,请参阅配额页面。

以下限制仅适用于请求处理程序的使用:

限制 数量
请求大小 32 MB
响应大小 32 MB
请求超时 取决于应用使用的扩缩类型
最大文件(应用文件和静态文件)总数 总共 10000 个
每个目录 1000 个
应用文件的最大大小 32 MB
静态文件的最大大小 32 MB
所有应用文件和静态文件的最大总大小 第 1 GB 免费
超出 1 GB 后,每 GB 每月收取 $ 0.026
待处理请求超时 10 秒
单个请求标头字段的大小上限 对于标准环境中的第二代运行时,大小上限为 8 千字节。对这些运行时的请求,如果标头字段超过 8 千字节,将返回 HTTP 400 错误。

请求限制

转发到应用服务器时,所有 HTTP/2 请求都将转换为 HTTP/1.1 请求。

响应限制

  • 动态响应上限为 32 MB。如果脚本处理程序生成的响应大于此限额,则服务器会发回一个包含 500 Internal Server Error 状态代码的空响应。此上限不适用于从旧式 Blobstore 或 Cloud Storage 提供数据的响应。

  • 对于第二代运行时,响应标头上限为 8 KB。超出此上限的响应标头将返回 HTTP 502 错误,并显示日志 upstream sent too big header while reading response header from upstream

请求标头

传入 HTTP 请求包含客户端发送的 HTTP 标头。为保证安全,部分标头在到达应用前由中间代理进行清理或修改。

如需了解详情,请参阅请求标头参考文档

处理请求超时

App Engine 针对那些具有短暂请求寿命(通常为几百毫秒)的应用进行了优化。高效的应用可以对大多数请求作出快速响应。响应缓慢的应用将无法配合 App Engine 的基础架构良好地进行扩缩。 为了确保此性能水平,系统施加了请求超时上限,每个应用都必须在此时间内响应。

如果您的应用超过此截止时间,App Engine 会中断请求处理程序。 Java 运行时环境通过抛出 com.google.apphosting.api.DeadlineExceededException 来中断 servlet。如果没有请求处理程序来捕获此异常,则运行时环境将向客户端返回 HTTP 500 服务器错误。

如果存在请求处理程序且捕获了 DeadlineExceededException,则运行时环境会为请求处理程序提供时间(少于一秒)以准备自定义响应。如果在引发异常以准备自定义响应后,请求处理程序花费的时间超过一秒,则会引发 HardDeadlineExceededError

DeadlineExceededExceptionsHardDeadlineExceededErrors 都会强行终止请求并停止实例。

如需了解距离时限还剩多少时间,应用可以导入 com.google.apphosting.api.ApiProxy 并调用 ApiProxy.getCurrentEnvironment().getRemainingMillis()。如果应用计划开始处理一些可能需要花费很长时间的工作,这样会非常有用;如果您知道处理一个工作单元需要五秒钟,但是 getRemainingMillis() 返回的时间较少,那么开始这个工作单元是没有意义的。

响应

App Engine 会使用请求对象和响应对象调用 servlet,然后等待 servlet 填充响应对象并返回。当 servlet 返回时,响应对象上的数据将发送给用户。

您生成的响应会受到一些大小限制,而且响应在返回客户端之前可以修改。

如需了解详情,请参阅请求响应参考文档

流式响应

App Engine 不支持流式响应(这种响应方式在处理请求的同时将数据以增量数据块的形式分多次发送到客户端),来自您的代码的所有数据会以前述方式收集,并作为单个 HTTP 响应一次性发送。

响应压缩

对于代码返回的响应,如果同时满足以下两个条件,App Engine 会压缩响应中的数据:

  • 请求包含 gzip 作为值的 Accept-Encoding 标头。
  • 响应中包含 HTML、CSS 或 JavaScript 等基于文本的数据。

对于由 App Engine 静态文件或目录处理程序返回的响应,如果满足以下所有条件,则响应数据会被压缩:

  • 该请求包含 Accept-Encoding,其中 gzip 作为其值之一。
  • 客户端能够接收压缩格式的响应数据。Google Frontend 会维护一个已知存在响应问题的客户端列表。即使请求标头包含 Accept-Encoding: gzip,这些客户端也不会从应用中的静态处理程序接收压缩数据。
  • 响应中包含 HTML、CSS 或 JavaScript 等基于文本的数据。

注意事项:

  • 客户端可以通过将 Accept-EncodingUser-Agent 请求标头都设置为 gzip 来强制压缩基于文本的内容类型。

  • 如果请求未在 Accept-Encoding 标头中指定 gzip,则 App Engine 不会压缩响应数据。

  • Google Frontend 缓存来自 App Engine 静态文件和目录处理程序的响应。根据不同的因素(例如,首先缓存的响应数据类型、您在响应中指定的 Vary 标头以及请求中包含的标头),客户端可以请求压缩的数据但接收未压缩的数据,反之亦然。如需了解详情,请参阅响应缓存

响应缓存

Google Frontend 以及用户的浏览器和其他中间缓存代理服务器将按照您在响应中指定的标准缓存标头来缓存应用的响应。您可以通过框架在代码中直接指定这些响应标头,也可以通过 App Engine 静态文件和目录处理程序指定这些响应标头。

在 Google Frontend 中,缓存键是请求的完整网址。

缓存静态内容

如需确保客户端可始终收到发布的静态内容更新,我们建议您从版本化目录(例如 css/v1/styles.css)传送静态内容。在缓存过期之前,Google Frontend 不会验证缓存(检查更新的内容)。即使缓存过期,缓存只有在请求网址中的内容发生更改时才会更新。

app.yaml 中设置的以下响应标头会影响 Google Frontend 缓存内容的方式和时间:

  • Cache-Control 应设置为 public,以便 Google Frontend 缓存内容;除非指定了 Cache-Control privateno-store 指令,否则 Google Frontend 也可能会缓存它。如果您未在 app.yaml 中设置此标头,App Engine 会自动为由静态文件或目录处理程序处理的所有响应添加此标头。如需了解详情,请参阅添加或替换标头

  • Vary:如需使缓存根据请求中发送的标头返回网址的不同响应,请在 Vary 响应标头设置以下一个或多个值:AcceptAccept-EncodingOriginX-Origin

    由于可能会产生高基数,因此系统不会为其他 Vary 值缓存数据。

    例如:

    1. 您指定了以下响应标头:

      Vary: Accept-Encoding

    2. 应用收到的请求包含 Accept-Encoding: gzip 标头。App Engine 返回压缩响应,并且 Google Frontend 会缓存响应数据的 Gzip 版本。包含 Accept-Encoding: gzip 标头的网址的所有后续请求都将接收来自缓存的 Gzip 数据,直到缓存失效(由于内容在缓存过期后会发生变化)。

    3. 应用收到的请求不包含 Accept-Encoding 标头。App Engine 返回未压缩的响应,并且 Google Frontend 会缓存响应数据的未压缩版本。不包含 Accept-Encoding 标头的网址所有后续请求都会接收来自缓存的压缩数据,直到缓存失效为止。

    如果您未指定 Vary 响应标头,则 Google Frontend 将为网址创建一个缓存条目,并将其用于所有请求(无论请求中的标头如何)。例如:

    1. 您未指定 Vary: Accept-Encoding 响应标头。
    2. 请求包含 Accept-Encoding: gzip 标头,系统会缓存响应数据的 Gzip 版本。
    3. 第二个请求不包含 Accept-Encoding: gzip 标头。但是,由于缓存包含响应数据的 Gzip 版本,因此,即使客户端请求未压缩的数据,该响应也会经过 Gzip 压缩。

请求中的标头也会影响缓存:

  • 如果请求包含 Authorization 标头,则 Google Frontend 不会缓存该内容。

缓存到期

默认情况下,App Engine 静态文件和目录处理程序添加到响应的缓存标头会指示客户端和 Web 代理(如 Google Frontend)使缓存在 10 分钟后过期。

在传输指定了到期时间的文件后,即使用户清除了自己的浏览器缓存,通常也无法将文件从 Web 代理缓存中清除。重新部署应用的新版本将不会重置任何缓存。因此,如果您打算修改某个静态文件,则应该设置较短的到期时间(不超过一个小时)。在大多数情况下,默认的 10 分钟到期时间是合适的。

您可以通过在 app.yaml 文件中指定 default_expiration 元素,更改所有静态文件和目录处理程序的默认到期时间。如需为个别处理程序设置特定的到期时间,请在 app.yaml 文件中的处理程序元素内指定 expiration 元素。

您在到期元素时间中指定的值将用于设置 Cache-ControlExpires HTTP 响应标头。

强制 HTTPS 连接

出于安全考虑,所有应用都应建议客户端使用 https 连接。如需指示浏览器为给定页面或整个网域使用 https 而不是 http,请在响应中设置 Strict-Transport-Security。例如:

Strict-Transport-Security: max-age=31536000; includeSubDomains
如需为应用传送的任何静态内容设置此标头,请将该标头添加到应用的静态文件和目录处理程序

大多数应用框架和 Web 服务器均支持为代码生成的响应设置此标头。如需了解 Spring Boot 中的 Strict-Transport-Security 标头,请参阅 HTTP 严格传输安全协议 (HSTS)

处理异步后台工作

后台工作是指您的应用在传送 HTTP 响应后为请求执行的任何工作。避免在应用中执行后台工作,并审核代码,以确保所有异步操作都会在传送响应之前完成。

对于长时间运行的作业,我们建议使用 Cloud Tasks。使用 Cloud Tasks 时,HTTP 请求会长期有效,并且仅在任何异步工作结束后返回响应。