Django 如何避免上传大文件时出现空闲连接超时?

Django 如何避免上传大文件时出现空闲连接超时?,django,nginx,amazon-s3,gunicorn,amazon-elb,Django,Nginx,Amazon S3,Gunicorn,Amazon Elb,以我们当前的架构为例: +---------------+ | Clients | | (API) | +-------+-------+

以我们当前的架构为例:

         +---------------+                             
         |    Clients    |                             
         |    (API)      |                             
         +-------+-------+                             
                 ∧                                     
                 ∨                                     
         +-------+-------+    +-----------------------+
         | Load Balancer |    |   Nginx               |
         | (AWS - ELB)   +<-->+   (Service Routing)   |
         +---------------+    +-----------------------+
                                          ∧            
                                          ∨            
                              +-----------------------+
                              |   Nginx               |
                              |   (Backend layer)     |
                              +-----------+-----------+
                                          ∧            
                                          ∨            
         -----------------    +-----------+-----------+
           File Storage       |       Gunicorn        |
           (AWS - S3)     <-->+       (Django)        |
         -----------------    +-----------------------+
+-------------+
|客户|
|(API)
+-------+-------+                             
∧                                     
∨                                     
+-------+-------+    +-----------------------+
|负载平衡器| | Nginx|
|(AWS-ELB)++(服务路由)|
+---------------+    +-----------------------+
∧            
∨            
+-----------------------+
|Nginx|
|(后端层)|
+-----------+-----------+
∧            
∨            
-----------------    +-----------+-----------+
文件存储| Gunicorn|
(AWS-S3)+(Django)|
-----------------    +-----------------------+
当客户机、移动设备或网络试图在我们的服务器上上传大文件(超过GB)时,通常会面临空闲连接超时。可以从他们的客户端库(例如,在iOS上),也可以从我们的负载平衡器

当客户端实际上载文件时,不会发生超时,因为连接不是“空闲”,正在传输字节。但我认为,当文件被传输到Nginx后端层,Django开始将文件上传到S3时,客户端和服务器之间的连接将处于空闲状态,直到上传完成


有没有办法防止这种情况发生?我应该在哪一层解决这个问题?

我也遇到过同样的问题,并通过在顶部使用来修复它。django队列存储所做的是,当接收到文件时,它会创建一个芹菜任务,将其上载到远程存储(如S3),同时,如果任何人访问了该文件,但该文件在S3上尚不可用,它会从本地文件系统提供该文件。通过这种方式,您不必等待文件上传到S3才能将响应发送回客户端


作为负载平衡器背后的应用程序,您可能希望使用共享文件系统,例如,以便使用上述方法。

您可以尝试跳过将文件上载到服务器并直接上载到s3,然后只获取应用程序的url


有一个应用程序可以实现这一点:您可以尝试一下。

您可以创建一个上载处理程序,将文件直接上载到s3。这样,您就不会遇到连接超时问题

我做了一些测试,它在我的情况下非常有效

例如,您必须使用boto启动新的多部分上传,并逐步发送块

不要忘记验证块大小。如果文件包含多个部分,则最小为5Mb。(S3限制)

如果您真的想直接上传到s3并避免连接超时,我认为这是django排队存储的最佳替代方案

您可能还需要创建自己的文件字段来正确管理文件,而不是第二次发送

下面的示例与S3BOToRage一起使用

S3_MINIMUM_PART_SIZE = 5242880


class S3FileUploadHandler(FileUploadHandler):
    chunk_size = setting('S3_FILE_UPLOAD_HANDLER_BUFFER_SIZE', S3_MINIMUM_PART_SIZE)

    def __init__(self, request=None):
        super(S3FileUploadHandler, self).__init__(request)
        self.file = None
        self.part_num = 1
        self.last_chunk = None
        self.multipart_upload = None

    def new_file(self, field_name, file_name, content_type, content_length, charset=None, content_type_extra=None):
        super(S3FileUploadHandler, self).new_file(field_name, file_name, content_type, content_length, charset, content_type_extra)
        self.file_name = "{}_{}".format(uuid.uuid4(), file_name)

        default_storage.bucket.new_key(self.file_name)

        self.multipart_upload = default_storage.bucket.initiate_multipart_upload(self.file_name)

    def receive_data_chunk(self, raw_data, start):
        buffer_size = sys.getsizeof(raw_data)

        if self.last_chunk:
            file_part = self.last_chunk

            if buffer_size < S3_MINIMUM_PART_SIZE:
                file_part += raw_data
                self.last_chunk = None
            else:
                self.last_chunk = raw_data

            self.upload_part(part=file_part)
        else:
            self.last_chunk = raw_data

    def upload_part(self, part):
        self.multipart_upload.upload_part_from_file(
            fp=StringIO(part),
            part_num=self.part_num,
            size=sys.getsizeof(part)
        )
        self.part_num += 1

    def file_complete(self, file_size):
        if self.last_chunk:
            self.upload_part(part=self.last_chunk)

        self.multipart_upload.complete_upload()
        self.file = default_storage.open(self.file_name)
        self.file.original_filename = self.original_filename

        return self.file
S3\u最小零件尺寸=5242880
类S3FileUploadHandler(FileUploadHandler):
chunk\u size=设置('S3\u文件\u上传\u处理程序\u缓冲区\u大小',S3\u最小\u部分\u大小)
定义初始化(self,请求=None):
超级(S3FileUploadHandler,self)。\uu初始化(请求)
self.file=None
self.part_num=1
self.last_chunk=无
self.multipart\u upload=无
定义新文件(self、字段名称、文件名称、内容类型、内容长度、字符集=None、内容类型=extra=None):
super(S3FileUploadHandler,self).新文件(字段名称、文件名称、内容类型、内容长度、字符集、内容类型额外)
self.file_name=“{}}{}”。格式(uuid.uuid4(),文件名)
默认存储.bucket.new\u密钥(self.file\u名称)
self.multipart\u upload=默认存储.bucket.initiate\u multipart\u upload(self.file\u名称)
def接收数据块(自身、原始数据、开始):
缓冲区大小=sys.getsizeof(原始数据)
如果self.last_块:
file\u part=self.last\u块
如果缓冲区大小小于S3最小部分大小:
文件\u部分+=原始\u数据
self.last_chunk=无
其他:
self.last\u chunk=原始\u数据
self.upload\u part(part=file\u part)
其他:
self.last\u chunk=原始\u数据
def上传_部件(自身,部件):
self.multipart\u upload.upload\u part\u from\u file(
fp=StringIO(部分),
part_num=self.part_num,
size=sys.getsizeof(部分)
)
self.part_num+=1
def文件完成(自身,文件大小):
如果self.last_块:
self.upload\u part(part=self.last\u块)
self.multipart\u upload.complete\u upload()
self.file=default\u storage.open(self.file\u名称)
self.file.original\u filename=self.original\u filename
返回self.file

您是否在NGINX conf中设置了客户端\u max\u body\u大小?哪个系统触发超时?ELB还是别的什么?ELB默认为60秒,但它是可配置的。在这种情况下,是客户端超时。您可以列出所有级别上已调整的所有超时相关设置吗?@Michael sqlbot我已经将该值增加到20分钟,但我认为这是一种黑客行为,因为等待一个大文件从我们的服务器上传到S3的行为不应该被视为“空闲”。此外,我无法控制客户端的空闲超时,因此我认为这不能完全解决问题。谢谢