配置预热请求以提高性能

在将应用代码加载到新创建的实例时,您可以使用预热请求来缩短请求与响应的延迟时间。

App Engine 经常需要将应用代码加载到全新实例中。以下情况下可能会加载实例:

  • 重新部署应用版本时。
  • 由于请求产生的负载超过当前运行中实例集的容量而创建新实例时。
  • 维护和维修底层基础架构或物理硬件时。

将应用代码加载到新实例会产生加载请求。加载请求可能会延长用户的请求延迟时间,但您可以使用预热请求来避免此延时。预热请求会在任何实际请求到达新实例之前,先将应用的代码加载到新实例中。

如果您为应用启用了预热请求,则 App Engine 会尝试检测您的应用何时需要新实例,并发出预热请求来对新实例进行初始化。但是,此类检测尝试并不适用于所有情况。因此,即使应用中启用了预热请求,您也可能会遇到加载请求。例如,如果您的应用目前没有任何流量,则对应用的第一个请求始终会是加载请求,而不是预热请求。

与任何其他发送至 App Engine 应用的请求一样,预热请求也会使用实例小时数。在大多数启用了预热请求的情况下,您不会注意到实例小时数的增加,因为您的应用只是在预热请求(而非加载请求)中进行初始化。如果您决定执行更多工作,例如在预热请求期间预先缓存,则使用的实例小时数可能会增加。如果您将 min_idle_instances 设置为大于 0,则在这些实例首次启动时,您可能会遇到预热请求,但在此之后这些实例将保持可用。

默认预热请求设置会导致所有 JAR 文件在内存中编入索引,并初始化应用和过滤器。

启用预热请求

App Engine 调度程序会使用预热请求,以便根据用户提供的配置来控制实例的自动扩缩。App Engine Java 运行时默认启用预热请求,因此 App Engine 会向 /_ah/warmup 发出 GET 请求,以便您可以根据需要进行响应并初始化应用的代码。您可以采用以下任一方法来对预热请求进行响应:

使用 <load-on-startup> Servlet
提供预热逻辑的最简单方法是在 web.xml 配置文件中将您自己的 Servlet 标记为 <load-on-startup>
使用 ServletContextListener
让您可以在通过预热请求或加载请求第一次调用任意 Servlet 之前运行自定义逻辑。
使用自定义预热 Servlet
自定义预热 Servlet 的 service 方法只会在处理预热请求时调用,在处理加载请求时不会调用。

您可能需要为 /_ah/warmup 实现自己的处理程序,具体取决于您选择的方法。

准备工作

启用预热请求后,若调度程序确定需要更多实例,便会启动实例。调度程序会使用预热请求来启动您的应用,即使应用不处理这些预热请求,您也会在日志中看到它们。

请注意,预热请求调用不一定会成功。在某些情况下,系统会改为发送加载请求:例如,如果实例是第一个被启动的实例,或者流量出现急剧上升。但是,如果启用了预热请求,系统将“尽力”尝试向已经预热的实例发送请求。

Java 8 应用默认启用预热请求。要启用预热请求,请将 - warmup 添加至 appengine-web.xml 中的 inbound_services 指令。由于预热会默认启用,因此只有在您先前已部署了一个应用,并且在 appengine-web.xml 中停用了预热请求时,才需要明确启用它们。如果是这种情况,您需要将 <warmup-requests-enabled> 值设置为 true,然后重新部署。

使用 <load-on-startup> Servlet

提供预热逻辑的最简单方法是在 web.xml 中将您自己的 Servlet 标记为 <load-on-startup>。此方法无需更改应用代码,并可在初始化您的应用时初始化所有指定 Servlet。

web.xml 文件中,对于要在启动时加载的 Servlet,请将 <load-on-startup>1</load-on-startup> 元素添加到 <servlet> 元素。例如:

<servlet>
  <servlet-name>my-servlet</servlet-name>
  <servlet-class>com.company.MyServlet</servlet-class>
  <load-on-startup>1</load-on-startup>
</servlet>

这些代码会加载指定的 Servlet 类并调用 Servlet 的 init() 方法。在处理任何实际请求前,预热请求会先初始化指定的 Servlet。但是,如果没有预热请求,则会在新实例第一次收到请求时注册 <load-on-startup> 中指定的 Servlet,从而生成加载请求。如前所述,应用需要新实例时,App Engine 可能并不会每次都发出预热请求。

使用 ServletContextListener

如果您在调用任何 Servlet 之前要运行自定义逻辑,请执行以下操作:

  1. web.xml 文件中注册一个 ServletContextListener

    <listener>
      <listener-class>com.company.MyListener</listener-class>
    </listener>
    
  2. 提供一个类以及您的 Servlet 和过滤器代码:

    public class MyListener implements ServletContextListener {
      public void contextInitialized(ServletContextEvent event) {
        // This will be invoked as part of a warmup request, or
        // the first user request if no warmup request was invoked.
      }
      public void contextDestroyed(ServletContextEvent event) {
        // App Engine does not currently invoke this method.
      }
    }
    

ServletContextListener 会在处理预热请求时运行。如果没有预热请求,它将在新实例第一次收到请求时运行。这可能会生成加载请求。

使用自定义预热 Servlet

自定义预热 Servlet 的 service 方法只会在处理预热请求时调用。通过将开销很大的逻辑放置到自定义预热 Servlet 中,您可以避免增加加载请求的加载时间。

如需创建自定义预热 Servlet,只需在 web.xml 中替换 _ah_warmup 的内置 Servlet 定义即可:

<servlet>
  <servlet-name>_ah_warmup</servlet-name>
  <servlet-class>com.company.MyWarmupServlet</servlet-class>
</servlet>