为什么有更多请求转到新(动态)实例而不是驻留实例?

时间:2017-07-14 13:06:58

标签: java google-app-engine startup autoscaling

在App Engine标准环境中使用自动扩展的Java应用程序。现在,缩放配置如下:

<instance-class>F2</instance-class>

<automatic-scaling>
    <min-idle-instances>1</min-idle-instances>

    <!-- ‘automatic’ is the default value. -->
    <max-idle-instances>2</max-idle-instances>

    <!-- ‘automatic’ is the default value. -->
    <min-pending-latency>2000ms</min-pending-latency>

    <max-pending-latency>8000ms</max-pending-latency>
    <max-concurrent-requests>60</max-concurrent-requests>
</automatic-scaling>

刚开始尝试F2实例,之前正在使用F1个实例。无论我如何配置自动缩放,似乎新创建的实例(在负载增加时创建)开始获取所有传入的请求,而常驻实例的负载非常轻。

这是为什么?当然,我无法实时监控流量(以及它去哪个实例),但每次看故事都似乎是一样的。我在下面提供了一些示例截图。

enter image description here

在下面的例子中,三个实例(这与上面的配置略有不同)是免费的,但GAE的负载均衡器选择将所有请求发送到具有最高延迟的实例!

enter image description here

还有一个例子:这是今天上午10:15:45开始的常驻实例的请求日志:

enter image description here

以及10秒后启动的动态实例的请求日志:

enter image description here

正如您所看到的那样,动态实例正在处理所有请求(到目前为止为1889年),而驻留者基本上处于空闲状态(在同一时间段内为7)。如果不是因为驻留实例似乎被销毁并且在创建新动态实例的时候重新创建 这一事实,那么这仍然可以。这意味着在一分钟左右的时间内,所有请求都会看到10-20秒的响应时间。

有人可以向我解释如何配置吗?

这就是我想要的:

  • 一个空闲实例应该能够在大多数时间处理负载(现在)。
  • 当有更多请求进入时,请启动其他实例。 准备好后,开始转移流量。

我正在尝试以低预算运行一个合理负载的网站,因此我尽量保持尽可能接近免费配额非常重要。

更新1

由于两个答案都突出地讨论了热身请求,我想我会在这里列出有关它的详细信息。我正在使用ServletContextListener来处理初始化。它执行以下操作(使用Guava的Stopwatch类收集时间,并且是我编写的/明确调用的代码):

  1. 注册客观化实体(1.449 s)
  2. Freemarker init 229 ms
  3. Firebase init 228.2 ms
  4. 除了我有Shiro过滤器,Objectify过滤器和Jersey过滤器(在泽西岛我通过显式注册类而不是给它一个扫描包来避免类路径扫描(我认为))在我的{{1 }}。不使用任何依赖注入来避免类路径扫描。

    web.xml请求需要7.8秒(从上述时间开始的请求)。但是,一个刚刚启动的动态实例提供服务的请求需要10秒多才能完成,尽管这些相同的调用在两分钟后需要200-700ms。那么除了我在/_ah/warmup明确做的事情之外,还有什么事情在后台发生?

    以下是日志的part 1和此处的日志part 2

2 个答案:

答案 0 :(得分:3)

  

似乎新创建的实例(在负载增加时创建)开始获取所有传入请求,而驻留实例的负载非常轻。

我的心智模型是Resident实例和预热请求仅在GAE实例的启动时间很长时才有用。 (我不确定这是不是意图,但那是我观察到的行为)

traffic is sent to resident instances while the new instances are being booted(和其他动态实例无法处理)。新实例启动并运行后,流量将路由到它,而不是驻留实例。

这意味着如果您的实例启动时间很短,那么常驻实例将不会做太多工作。 F2可以在~250ms(by my testing)内启动,因此如果您的平均响应延迟为2000ms,那么新的动态实例将在驻留实例完成处理请求之前完全启动。因此,它将准备好处理后续请求而不是常驻请求。

这似乎遵循您所看到的行为模式。

您可以通过查看stackdriver and logging separate out your response time vs boot time的方式来确认这一点。如果启动时间非常小,那么常驻实例可能对您没有多大帮助。

  

但是GAE的负载均衡器选择将所有请求发送到具有最高延迟的实例!

遗憾的是,关于GAE如何决定向哪个实例发送新数据包的信息不多。我发现的只有How instances are managedscheduling settings,它们更多地讨论了何时引导新实例的参数。

我知道这不是你提出的问题,但2000ms的响应时间可能会导致这个问题吗?如果您的min-pending-latency设置为2000,则新请求将在队列中停留2000ms,然后才会生成新实例。但如果它以串行方式进行服务(线程安全关闭),那么1500到2000之间的响应时间仍然可以得到正确的服务。

我建议启用threadsafe以查看是否有助于该方案,并添加一些custom tracing,因为代码正在做一些您无法查看的奇怪事项。

答案 1 :(得分:2)

空闲实例的作用不是处理通常的流量,而是能够处理溢出 - 已经运行的动态实例(如果有的话)无法处理的临时流量峰值,直到正在开始新的实例。

从某种意义上说,他们称之为空闲的原因 - 大多数时候他们只是闲着。在预算压力下,IMHO放弃空闲实例是首先要做的事情之一。

也许相关:In Google app engine only one instance handling most of requests

旁注:它不是the GAE's load balancer chooses to send all requests to the instance with the highest latency!。实际上,该首选实例的延迟是最高的,因为它是获得大部分流量的延迟。

为防止GAE在准备好处理新实例之前将流量发送到新实例,您需要配置(并正确处理)warmup requests

  

预热请求是加载的特定类型的加载请求   在任何直播之前,应用程序代码提前到实例中   提出要求。要了解有关如何使用预热请求的更多信息,请参阅   Warmup Requests。无法接收手动或基本缩放实例   /_ah/warmup请求。

您可能还想对此答案进行思考:How do you keep a running instance for Google App Engine。基本上尝试通过阻止它们通过cron作业长时间保持空闲来保持动态实例无限期运行。

对于刚启动新动态实例后驻留实例的明显重启似乎有点奇怪。我不会太担心 - 它可能只是某种播放安全的刷新策略:那时空闲实例的需求将是最低的,因为新启动的动态实例是最少的可能会被传入的请求所淹没。