慢慢访问Django的request.body

时间:2014-05-31 11:33:05

标签: python django performance apache request

当某些移动客户端提交时,有时候这一行Django应用程序(使用Apache / mod_wsgi托管)需要花费大量时间来执行(例如,由New Relic测量,例如,请求处理的时间为99%) :

raw_body = request.body

(其中request是传入请求)

我的问题:

  1. 什么可能会减慢对request.body的访问速度?
  2. 在调用Django直到客户端发送整个有效负载之前,Apache要等待的正确配置是什么?也许问题出在Apache配置中。
  3. Django的body attribute in HttpRequest is a property,所以这真的可以解决那里真正做的事情以及如何在Django应用程序之外实现它,如果可能的话。我希望Apache在将其发送到Django app之前等待完整请求。

4 个答案:

答案 0 :(得分:9)

关于(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 :(得分:9)

有两种方法可以解决这个问题。

您可以使用>=2.3中提供的mod_buffer,并将BufferSize更改为最大预期有效负载大小。这应该使Apache在内存中保留请求,直到它完成发送或者到达缓冲区。

对于较旧的Apache版本< 2.3,您可以将mod_proxyProxyIOBufferSizeProxyReceiveBufferSize和环回虚拟主机结合使用。这涉及将您的真实虚拟主机置于环回接口上,并暴露连接回真实虚拟主机的代理虚拟主机。这样做的缺点是它使用了两倍的套接字,并且可能使resource calculation变得困难。

但是,最理想的选择是在L4/L7负载均衡器上启用请求/响应缓冲。例如,haproxy允许您根据req_len添加rules,同样适用于nginx。大多数优秀的商业负载均衡器也可以选择在发送之前缓冲请求。

所有这三种方法都依赖于缓冲完整的请求/响应有效负载,并且根据您的用例和可用资源存在性能考虑因素。您可以将整个有效负载缓存在内存中,但这可能会大大降低最大并发连接数。您可以选择将有效负载写入本地存储(最好是SSD),但是您会受到IO容量的限制。

您还需要考虑文件上传,因为这些不适合基于内存的有效负载缓冲。在大多数情况下,您可以在网络服务器中处理上传请求,例如HttpUploadModule,然后查询upload progress的nginx,而不是直接在WSGI中处理。如果您正在缓冲负载均衡器,那么您可能希望从缓冲规则中排除文件上传。

您需要了解why this is happening,并且在发送响应和接收请求时都存在此问题。保持这些保护也是一个好主意,不仅仅是为了可扩展性,而是为了security reasons

答案 2 :(得分:0)

我担心问题可能在于您传输的数据量以及可能是连接缓慢。另请注意,上传带宽通常远低于下载带宽。

正如已经指出的那样,当你使用request.body时,Django将等待整个主体从客户端完全传输,并在服务器上内存(或在磁盘上,根据配置和大小)。

如果客户端连接到连接到服务器本身的WiFi接入点,我建议你尝试使用相同的请求会发生什么,看看它是否有所改进。如果这是不可能的,也许只需在客户端上运行像speedtest.net这样的工具,获取请求大小并进行数学计算,看看理论上需要多长时间(我预计时间大约为20) % 更多)。请注意,网络速度通常以每秒位数为单位,而文件大小则以字节为单位。

在某些情况下,如果需要对数据进行大量处理,那么read()请求可能会很方便,并且可以随时进行计算,或者可能直接传递request个对象任何可以从所谓的“类文件对象”而不是字符串中读取的函数。

但是,在您的具体情况下,我担心这只会影响从网络接收身体所花费的1%的时间。

编辑:

抱歉,现在我已经注意到赏金中的额外描述了。我恐怕无法帮助你,但请问,有什么意义?我猜这只会节省一点服务器资源,以保持python线程闲置一段时间,而不会对请求产生任何显着的性能提升......

答案 3 :(得分:0)

查看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

例如,您可以一次读取一行。