gUnicorn/Flask/GAE - 启动了两个进程来处理相同的 http 请求

问题描述 投票:0回答:1

我在 Google AppEngine(Python39 标准环境)上有一个应用程序,在 gUnicorn 和 Flask 上运行。我正在从客户端应用程序向服务器发出长时间运行操作的请求,并看到该请求被处理了两次。第二个进程(工人)在第一个进程工作一段时间(一个半小时)后开始。

我不确定它是与 gUnicorn 特别相关还是与 GAE 有关。

服务器控制器一开始就有日志记录:

@app.route("/api/campaign/generate", methods=["GET"])
def campaign_generate():
  logging.info('Entering campaign_generate');
  # some very long processing here

通过单击 UI 应用程序中的按钮来调用控制器。我在浏览器的 DevTools 中检查了网络,只触发了一个请求。我可以看到,在执行工作线程时,服务器日志中只有一个请求(更多内容见下文)。

整个app.yaml是这样的:

runtime: python39
default_expiration: 0
instance_class: B2
basic_scaling:
  max_instances: 1
entrypoint: gunicorn -b :$PORT server.server:app --timeout 0 --workers 2

所以我有 2 个具有无限超时的工作人员,最大实例 = 1 的基本扩展。 我希望当应用程序正在处理一个长时间运行的操作的请求时,另一个工作人员可以提供服务。 我不希望第二个工作人员用来处理相同的请求,这是无稽之谈(只要用户不会从另一个浏览器启动另一个操作)。
由于 timeout=0 我预计 gUnicorn 将无限期地等待,直到控制器完成。只有一件事会阻碍 GAE'e 超时。但由于基本缩放,它是 24 小时。所以我预计该应用程序应该可以毫无问题地处理请求几个小时。

但我看到的是,在处理请求一段时间后,另一个执行开始了。以下是我在 Cloud Logging 中看到的简化日志:

13:00:58 GET /api/campaign/generate
13:00:59 Entering campaign_generate
..skipped
13:39:13 Starting generating zip-archive (it's something that takes a while)
14:25:49 Entering campaign_generate

因此,在当前请求到来后的 1:25、14:25,同一请求的另一个处理开始了!
现在有两个请求处理并行运行。 不用说,这会增加内存压力并使执行时间加倍。

当第一个“worker”完成处理(在我们的示例中为 14:29:28)时,其结果不会返回给客户端。看起来 gUnicorn 或 GAE 只是放弃了第一个请求。并且客户端必须等待第二个worker完成处理。

为什么会这样? 我该如何解决它?

关于日志中的http请求记录。
当处理处于活动状态时,我确实在 Cloud Logging 中只看到一个请求(第一个请求),即使在第二次调用控制器之后(日志中出现“输入营销活动生成”),也没有任何新的 GET 请求日志。但一切完成后(实际上第二次处理返回了响应),出现了第二个神秘的 GET 请求。因此,从技术上讲,完成所有操作后,从服务器日志视图(云日志)来看,似乎有来自客户端的两个后续请求。但没有!只有一个,我可以在浏览器的 DevTools 中看到它。
这两个请求具有不同的traceId和requestId http标头。

很难理解发生了什么,我尝试在本地运行应用程序(在相同的数据上),但它按预期工作。

flask google-app-engine gunicorn
1个回答
0
投票

鉴于您没有在入口点命令中定义工作程序类,默认工作程序类是

sync
类型(请参阅gunicorn 文档),这可能导致您观察到的问题。如果您有长时间运行的请求,您应该考虑使用
gthreads
工作人员。

Sync(同步)Workers:Gunicorn 中的默认worker 类型。每个工作人员一次只能处理一个请求。如果同步工作线程正忙于处理长时间运行的请求,则在完成之前它将无法处理另一个请求。如果主进程检测到工作线程没有响应(由于长时间运行的操作),它可能会认为工作线程被卡住并重新启动它,从而导致您所看到的行为。

Gthread(线程)Workers:这些workers可以使用线程同时处理多个请求。这种类型更适合 I/O 密集型应用程序,并且可以更有效地利用资源来处理长时间运行的请求。

要开始使用

gthread
工作人员,您所需要做的就是将
entrypoint
中的
app.yaml
更新为以下内容:

entrypoint: gunicorn -b :$PORT server.server:app --timeout 0 --workers 2 --worker-class gthread
© www.soinside.com 2019 - 2024. All rights reserved.