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

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

F2  1  2  2000ms 8000ms 60  

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

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

在此处输入图像描述

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

在此处输入图像描述

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

在此处输入图像描述

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

在此处输入图像描述

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

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

这就是我想要的:

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

我试图在预算不足的情况下运行一个合理负载的网站,所以我尽量保持尽可能接近免费配额是很重要的。

更新1

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

  1. 注册Objectify实体(1.449 s)
  2. Freemarker init 229 ms
  3. Firebase init 228.2 ms

除了我有Shirofilter,Objectifyfilter和泽西filter(在泽西岛我通过显式注册类而不是给它一个扫描包来避免类路径扫描(我认为))在我的web.xml配置。 不使用任何dependency injection来避免类路径扫描。

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

这是日志的第1部分 ,这是日志的第2部分 。

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

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

也许相关: 在Google应用引擎中,只有一个实例处理大多数请求

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

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

预热请求是一种特定类型的加载请求,可在任何实时请求发出之前将应用程序代码提前加载到实例中。 要了解有关如何使用预热请求的更多信息,请参阅预热请求 。 手动或基本扩展实例不会收到/_ah/warmup请求。

您可能还想对此答案进行思考: 如何为Google App Engine保留正在运行的实例 。 基本上尝试通过阻止它们通过cron作业长时间保持空闲来保持动态实例无限期运行。

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

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

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

即, 在引导新实例时将流量发送到驻留实例 (并且其他动态实例无法处理它)。 新实例启动并运行后,流量将路由到它,而不是驻留实例。

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

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

您可以通过查看stackdriver和logging如何将响应时间与启动时间分开来确认这一点。 如果启动时间非常小,那么常驻实例可能对您没有多大帮助。

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

遗憾的是,关于GAE如何决定向哪个实例发送新数据包的信息不多。 我找到的只是如何管理实例和调度设置 ,它们更多地讨论了何时引导新实例的参数。

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

我建议打开线程安全 ,看看是否有助于该场景,并添加一些自定义跟踪,因为代码正在做一些奇怪的事情,你没有可见性。