nginx + fastCGI + Django - 发送给客户端的响应中出现数据损坏

nginx + fastCGI + Django - 发送给客户端的响应中出现数据损坏

我使用 FastCGI 在 nginx 后面运行 Django。我发现在发送给客户端的某些响应中,响应中间发生了随机数据损坏(中间可能有几百个字节左右)。

此时,我已将问题缩小到 nginx 的 FastCGI 处理程序或 Django 的 FastCGI 处理程序中的错误(即可能是 flup 中的错误),因为当我以独立(即runserver)模式运行 Django 服务器时从未发生过此问题。它只发生在 FastCGI 模式下。

其他有趣的趋势:

  • 它往往发生在较大的响应上。当客户端首次登录时,会向他们发送一堆 1MB 的块以将其同步到服务器数据库。在第一次同步之后,响应会小得多(通常一次只有几 KB)。损坏似乎总是发生在开始时发送的那些 1MB 块上。

  • 当客户端通过 LAN(即低延迟、高带宽连接)连接到服务器时,这种情况更常发生。这让我认为 nginx 或 flup 中存在某种竞争条件,并且数据速率增加会加剧这种情况。

现在,我必须通过在响应标头中放置额外的 SHA1 摘要来解决这个问题,并让客户端拒绝标头与正文校验和不匹配的响应,但这是一种糟糕的解决方案。

有没有其他人经历过类似的事情,或者有什么指点可以告诉我如何识别是 flup 还是 nginx 的问题,以便我可以向相应的团队提交错误报告?

在此先感谢您的帮助。

注意:我之前也在这里发布了 lighttpd + FastCGI + Django 中的类似错误:https://stackoverflow.com/questions/3714489/lighttpd-fastcgi-django-truncated-response-sent-to-client-due-to-unexpected...即使这不是同一件事(截断与损坏),它开始看起来常见的罪魁祸首是 flup / Django 而不是 Web 服务器。

编辑:我还应该注意我的环境:

  • Mac Mini 上的 OSX 10.6.6

  • Python 2.6.1(系统)

  • Django 1.3(来自官方 tarball)

  • flup 1.0.2(来自 flup 网站上的 Python egg)

  • nginx +ssl 1.0.0(来自 Macports)

编辑:为了回应 Jerzyk 的评论,组装响应的代码路径如下所示(为简洁起见进行了编辑):

# This returns an objc NSData object, which is an array.array 
# when pushed through the PyObjC bridge
ret = handler( request ) 

response = HttpResponse( ret )
response[ "Content-Length" ] = len( ret )
return response

我认为 Content-Length 不可能是错误的,而且据我所知,没有办法将 Django HttpResponse 对象明确标记为二进制而不是文本。此外,由于问题只是间歇性发生,我认为这无法解释它,否则你大概会在每次请求中看到它。

编辑@ionelmc:您必须在 Django 中设置 Content-Length - nginx 不会为您设置此功能,如以下示例所示,一旦我明确禁用设置 Content-Length:

$ curl -i http://localhost/io/ping
HTTP/1.1 200 OK
Server: nginx/1.0.0
Date: Thu, 23 Jun 2011 13:37:14 GMT
Content-Type: text/html; charset=utf-8
Transfer-Encoding: chunked
Connection: keep-alive

AKSJDHAKLSJDHKLJAHSD

答案1

您是否对 fastcgi 响应启用了任何类型的 nginx 缓存(bypass / no_cache)指令?

在 nginx'1.0.3 Changenotes 中,他们修复了响应损坏:

错误修复:如果“proxy/fastcgi/scgi/uwsgi_cache_bypass”和“proxy/fastcgi/scgi/uwsgi_no_cache”指令值不同,缓存响应可能会被破坏;该错误出现在 0.8.46 中。

来源: http://nginx.org/en/CHANGES( 1.0.3. 节 )

答案2

也许仅当输出至少包含一个 UTF-8 字符时才会发生偶尔的损坏。

内容长度和字符串长度不是一回事,因为一个UTF-8字符可以包含2到5个字节。

答案3

进一步解决此问题的一种方法是:

  • 让 nginx 和 django 在不同的硬件上运行(这样你就可以轻松捕获流量)
  • 捕获从客户端到--/-> nginx 和 nginx --/-> django 的流量(即使用 wireshark)

一旦您在客户端检测到错误(基于 sha1),请转到网络捕获,查看记录的(TCP)流并尝试查找问题是由 nginx 生成的还是(直接)来自 django。

答案4

我有非常相似的问题自从我使用此设置以来,这个问题一直困扰着我。像您一样,我使用 FastCGI、Nginx 和 macOS,并在大型请求中发现随机损坏(这大约占 1.5 MB 文档请求的 2%)。

我能够通过将 PHP-FPM(在我的情况下)和 Nginx 之间的 FastCGI 连接切换到 TCP 上的 Unix 套接字来解决我的问题。我不知道哪个部分导致了损坏,但避免使用内部 TCP 连接确实解决了这个问题。

相关内容