当客户端连接不稳定时,在 heroku 上运行的 django 中的工作人员会挂在帖子上

Posted

技术标签:

【中文标题】当客户端连接不稳定时,在 heroku 上运行的 django 中的工作人员会挂在帖子上【英文标题】:Workers in django run on heroku are hanging on post when client has flaky connection 【发布时间】:2012-08-25 00:22:45 【问题描述】:

我们在 heroku 上运行 django/gunicorn 服务器。我们的大多数用户所在的国家/地区的移动网络都不是那么好,因此他们的连接经常不稳定。

我们的大多数请求都是来自移动设备的“原始帖子”,而且似乎即使 POST 请求未完全传输,请求也已被发送到 gunicorn 工作人员处理。当工作人员尝试处理请求并读取数据时,它只是挂起等待剩余的数据。虽然这种行为对于在“流”模式下读取文件/图像数据是有意义的,但在我们的案例中却没有意义,因为我们所有的帖子都相对较小,并且可以很容易地被整个 Web 服务器读取,然后才转发到我们的 gunicorn工人。

当我们有许多这样的并行请求时,这种提前切换会导致麻烦 - 因为所有工作人员都可能被阻塞。目前,我们通过增加工人/测功机的数量来解决这个问题,但这非常昂贵。我找不到任何方法来强制 Web 服务器或 gunicorn 等待,并且只有在请求完全传输后才将请求转发给工作人员。

有没有办法让 heroku 的 web 服务器/gunicorn 仅在请求从客户端完全传输(完全被服务器接收)时才将请求传输给 gunicorn worker?

一些示例代码(我们添加了 newrelic 'per-instruction' 跟踪以确保这是导致问题的确切行):

def syncGameState(request):
    transaction = agent.current_transaction()
    with agent.FunctionTrace(transaction, "syncGameState_raw_post_data", 'Python/EndPoint'):
        data = request.raw_post_data
    with agent.FunctionTrace(transaction, "syncGameState_gameStateSyncRequest", 'Python/EndPoint'):
        sync_request = sync_pb2.gameStateSyncRequest()
    with agent.FunctionTrace(transaction, "syncGameState_ParseFromString", 'Python/EndPoint'):
        sync_request.ParseFromString(data)

以下是此示例慢速请求的 New Relic 测量结果(它是一个包含 7K 数据的 POST)。读取 POST 需要 99% 的方法时间....

【问题讨论】:

我认为 heroku 超时是 10 秒。我没有测试它,但也许你可以在Procfile中更改gunicorn timeout:web: gunicorn hellodjango.wsgi -t 3 -b 0.0.0.0:$PORT 默认为 30 秒。我们已经在 gunicorn 中设置了超时……但问题是,在这种情况下,我会取消请求,否则(即使 20 秒后)会成功,我真的不想这样做。我想做的不是在请求通过网络传输时阻止我的工作人员。 我对你的帖子感到困惑。当您谈论工人时,您谈论的是“网络测功机”还是“工人测功机”?传输数据时不应锁定“工人测功机”,因为收集数据的是“网络测功机”。它不是? (另外,拜托,你能告诉我那个慢国家是什么吗?) 我说的是gunicorn工人。有几个测功机,每个测功机上都有一个 gunicorn 正在运行,但它通常会使用 -w 选项产生几个工人:gunicorn configuration。就我而言,我现在将其设置为 -w 12。这些是单独的进程(gunicorn 的子进程)。路由到特定 dyno 的每个请求都通过 gunicorn 路由到其中一个工作人员。似乎收到处理请求后的工作人员在读取数据时被“持有”(同时读取 raw_post_data 属性)。 也许您可以在原始数据分配之前设置警报并在分配结束时禁用它。 docs.python.org/library/signal.html#example 。超时信号是一个简单的 SIGTERM 信号。 【参考方案1】:

You might want to give this article a read 并调查一个请求缓冲 HTTP 服务器,例如 Waitress。

【讨论】:

【参考方案2】:

在我看来,这里真正的问题是 gunicorn 正在阻塞。这是因为 gunicorn(默认情况下)使用同步工作者来运行您的任务。这意味着当 Web 请求命中 gunicorn 时,它将阻塞,直到它返回响应 - 在您的情况下,很长一段时间。

要解决这个问题,您可以使用 gevent 和 gunicorn 来执行非阻塞 IO。由于您的大部分时间都花在了 IO 上,这将确保 gunicorn 可以并行处理更多的 Web 请求。

要将 gevent 与 gunicorn 一起使用,您需要安装 gevent (pip install -U gevent),并通过添加:gunicorn -k gevent 更改您的 gunicorn 启动命令(这将告诉 gunicorn 使用 gevent 作为工作器)。

【讨论】:

我确实是正确的方向(但不完全正确),但走这条路会使应用程序更加复杂。我必须将请求读取过程(因此从请求中获取发布数据)与请求处理分开。这将能够运行更多的并行读取线程(不需要太多内存)和一些较少数量的处理进程。您建议的默认设置不会真正提供,因为当我有相同的进程从 post 读取并处理它时 - 我有同样的问题(我不能过多地增加进程数)

以上是关于当客户端连接不稳定时,在 heroku 上运行的 django 中的工作人员会挂在帖子上的主要内容,如果未能解决你的问题,请参考以下文章

WebSocket SocketIO 连接不适用于 Heroku 上的 NestJS 服务器并在 Vercel 上反应客户端

Node.js + Mongoose 可以在本地工作,但不能在 Heroku 上工作

如何从反应客户端向运行在 Heroku 平台上的表达服务器发出 API 请求

Heroku 上的 Sinatra/Thin 未检测到 HTTP 流连接 (SSE) 客户端断开连接

使用 Multer 将图像上传到 mongoDB 时 Heroku 出错

Heroku postgresql 查询在 Heroku 上不起作用,但在本地工作