访问 Django 的 request.body 慢
Posted
技术标签:
【中文标题】访问 Django 的 request.body 慢【英文标题】:Slow access to Django's request.body 【发布时间】:2014-07-21 02:19:24 【问题描述】:当某些移动客户端提交时,有时这行 Django 应用程序(使用 Apache/mod_wsgi 托管)需要很长时间才能执行(例如,按照 New Relic 衡量,99% 的请求处理时间为 6 秒) :
raw_body = request.body
(其中request
是传入请求)
我的问题:
-
是什么导致访问
request.body
的速度如此缓慢?
Apache 在调用 Django 之前等待客户端发送整个有效负载的正确配置是什么?可能问题出在 Apache 配置中。
Django 的body
attribute in HttpRequest
is a property,这样就真正解决了那里真正在做什么以及如何在 Django 应用程序之外实现它,如果可能的话。我希望 Apache 在将请求发送到 Django 应用之前等待完整请求。
【问题讨论】:
【参考方案1】:关于 (1),只要请求的标头可用,Apache 就会将控制权传递给 mod_wsgi 处理程序,然后 mod_wsgi 会将控制权传递给 Python。 request.body
的内部实现然后调用 read()
方法,该方法最终调用 mod_wsgi 中的实现,requests the request's body from Apache 如果 Apache 还没有完全接收到它,则阻塞直到它可用。
关于 (2),仅使用 mod_wsgi 是不可能的。至少,the hook processing incoming requests 没有提供阻塞机制,直到完整的请求可用。另一位发帖者建议在a response to this duplicate question 中使用 nginx 作为代理。
【讨论】:
(1) 不幸的是,我怀疑这一点(替代方案是服务器发生了一些非常奇怪的事情),所以感谢您指出源代码中的确切位置。 (2) 反向代理可能是个好主意,感谢您指出这一点。如果 nginx 不仅用作代理,而是将 uWSGI 配置为托管 Django 应用程序,您能否判断它是否也能够保留请求?我想是的。 我不是专家,但 AFAIK:nginx 在内部调用主体$request_body
,它的读取方式至少部分是 configurable。该变量的文档指出它是passed on to uwsgi,这听起来好像总是启用预缓冲。我不知道有多少请求被缓冲了。 read_request_body 在这里可能会有所帮助。
也许在这里设置uWSGI post-buffering
option 会有所帮助?在调用应用程序本身之前让 uWSGI 缓冲请求正文。【参考方案2】:
有两种方法可以在 Apache 中解决此问题。
您可以使用mod_buffer,在>=2.3
中可用,并将BufferSize
更改为最大预期有效负载大小。这应该使 Apache 将请求保存在内存中,直到它完成发送或到达缓冲区。
对于较旧的 Apache 版本 < 2.3
,您可以将 mod_proxy 与 ProxyIOBufferSize
、ProxyReceiveBufferSize
和环回虚拟主机结合使用。这涉及将您的真实虚拟主机放在环回接口上,并公开一个连接回真实虚拟主机的代理虚拟主机。这样做的缺点是它使用了两倍的套接字,并且会使resource calculation 变得困难。
但是,最理想的选择是在您的 L4/L7 负载平衡器上启用请求/响应缓冲。例如,haproxy
允许您基于req_len
添加rules,nginx 也是如此。大多数优秀的商业负载均衡器还可以选择在发送之前缓冲请求。
所有三种方法都依赖于缓冲完整的请求/响应负载,并且根据您的用例和可用资源,存在性能考虑因素。您可以将整个有效负载缓存在内存中,但这可能会大大降低您的最大并发连接数。您可以选择将有效负载写入本地存储(最好是 SSD),但您会受到 IO 容量的限制。
您还需要考虑文件上传,因为这些不适合基于内存的有效负载缓冲。在大多数情况下,您会在您的网络服务器中处理上传请求,例如HttpUploadModule,然后在 nginx 中查询upload progress,而不是直接在WSGI 中处理它。如果您在负载均衡器上进行缓冲,那么您可能希望从缓冲规则中排除文件上传。
您需要了解why this is happening,并且在发送响应和接收请求时都存在此问题。设置这些保护措施也是一个好主意,不仅是为了可扩展性,还为了security reasons。
【讨论】:
很酷的答案!我赞成并接受,尽管the other answer 也不错。如有必要,将验证答案并更改选择。谢谢! 没问题,如果您需要任何进一步的说明,请告诉我:)【参考方案3】:恐怕问题可能出在您正在传输的数据量上,并且可能是连接速度较慢。另请注意,上传带宽通常远小于下载带宽。
正如已经指出的,当您使用request.body
时,Django 将等待整个主体从客户端完全传输并在服务器上的内存中(或磁盘上,根据配置和大小)可用。
如果客户端连接到连接到服务器本身的 WiFi 接入点,我建议您尝试使用相同的请求会发生什么,看看它是否有很大的改进。如果这是不可能的,也许只是在客户端上运行一个像 speedtest.net 这样的工具,获取请求大小并进行数学运算,看看理论上需要多少时间(我希望测量的时间或多或少 20 % 更多的)。请注意,网络速度通常以每秒比特数为单位,而文件大小以字节为单位。
在某些情况下,如果需要对数据进行大量处理,则可以方便地read()
请求并随时随地进行计算,或者直接将request
对象传递给任何函数可以从所谓的“类文件对象”而不是字符串中读取。
但是,在您的具体情况下,恐怕这只会影响 1% 的未用于从网络接收正文的时间。
编辑:
抱歉,我现在才注意到赏金中的额外描述。恐怕我帮不了你,但请问,这有什么意义?我猜这只会节省一点点服务器资源,以使 python 线程保持空闲一段时间,而对请求没有任何明显的性能提升......
【讨论】:
要回答您关于“有什么意义”的问题,请阅读我接受的答案。【参考方案4】:查看 Django 源代码,看起来当您调用 request.body
时实际发生的情况是请求正文通过从流中读取而加载到内存中。
https://github.com/django/django/blob/stable/1.4.x/django/http/init.py#L390-L392
很可能如果请求很大,所花费的时间实际上只是将其加载到内存中。 Django 在请求上有一些方法可以将主体作为流处理,这取决于所使用的内容究竟是什么,可以让您更有效地处理请求。
https://docs.djangoproject.com/en/dev/ref/request-response/#django.http.HttpRequest.read
例如,您可以一次读取一行。
【讨论】:
负载有时有 150k 字节,加载需要 8 秒。是的,访问request.body
会读取请求内容。不幸的是,无论如何我一次都需要它,我正在寻找一种在传递给 Django 应用程序之前完全接收到请求的方式。我认为***.com/a/24039774/548696 是最接近我需要的答案。以上是关于访问 Django 的 request.body 慢的主要内容,如果未能解决你的问题,请参考以下文章
Django json.loads(request.body) 给出错误期望值:第 1 行第 1 列
尝试在 Axios GET 的正文中发送数据以在 Django 后端使用,但 request.body 的打印为空
django:request.bodyrequest.POSTrequest.data