最近,需要用django来处理和传输相对大的文件,发现客户端无法正确完整的收到传输的文件。
当使用 python manage.py runserver测试服务时没有问题。 但是,当使用nginx + uwsgi开启web服务时,无法在客户端获得完整的响应。 同时,uwsgi.log中存在错误日志,并有常规日志通知uswgi已完成请求处理:
Fri Feb 88 88:88:88 2088 - uwsgi_response_write_body_do() TIMEOUT !!!
OSError: write error
当uwsgi输出这些日志时,客户端仍会接收响应数据,直到接收到的大小等于服务器日志中生成的大小为止。 并且生成的大小小于响应的整个大小。 所以我认为Nginx有很大的缓冲空间。 Uwsgi会主动向Nginx写入响应,并且速度非常快,因为它们位于同一台计算机上。 但是Nginx将响应写入客户端的速度很慢。 如果Nginx将响应数据存储在缓冲区中,则在缓冲区已满时uwsgi的写请求将被阻止。 如果缓冲区很大,则uwsgi将等待很长时间,然后Nginx清除其缓冲区并写入请求超时。
当uwsgi完成处理并关闭响应流后,Nginx仍可以将数据写入客户端。
根据 ngx_http_uwsgi_module的文档 :
启用缓冲后,nginx会尽快从uwsgi服务器收到响应,并将其保存到uwsgi_buffer_size和uwsgi_buffers指令设置的缓冲区中。 如果整个响应都无法容纳到内存中,则可以将一部分响应保存到磁盘上的临时文件中。 写入临时文件由uwsgi_max_temp_file_size和uwsgi_temp_file_write_size指令控制。
有有两种形式的缓存,分别是内存缓冲区和临时文件缓冲区。 内存缓冲区的默认大小只有几kb。 但是,临时文件缓冲区的默认最大大小为1024MB,大约等于uwsgi日志中生成的大小。
为了解决这个问题,我们可以禁用临时文件缓冲区,在nginx配置文件http服务内写入:
uwsgi_max_temp_file_size 0;
内存缓冲非常小通常不会有什么问题,因为它不会阻塞uwsgi的写请求很多时间。
但是,如果Nginx与客户端之间的传输速度远慢于Nginx与uswgi之间的传输速度,那么无论缓冲区有多小,这个问题可能还是会出现。
在Django和uwsgi的实现中,uwsgi读取数据块并写入响应流,如果块大小足够大并且到客户端的传输速度足够慢,即使禁用了Nginx中的所有缓冲区,也会出现问题。 因为该块可以认为是缓冲区的另一种形式。
所以,如果要彻底解决问题,应该同时禁用 uwsgi 和nginx的缓冲区。