2016-09-21 9 views
11

は、私たちの現在のアーキテクチャを考えてみましょう:大きなファイルをアップロードするときにアイドル状態の接続タイムアウトが発生しないようにするにはどうすればよいですか?

  +---------------+        
     | Clients |        
     | (API)  |        
     +-------+-------+        
       ∧          
       ∨          
     +-------+-------+ +-----------------------+ 
     | Load Balancer | | Nginx    | 
     | (AWS - ELB) +<-->+ (Service Routing) | 
     +---------------+ +-----------------------+ 
              ∧    
              ∨    
           +-----------------------+ 
           | Nginx    | 
           | (Backend layer)  | 
           +-----------+-----------+ 
              ∧    
              ∨    
     ----------------- +-----------+-----------+ 
      File Storage  |  Gunicorn  | 
      (AWS - S3)  <-->+  (Django)  | 
     ----------------- +-----------------------+ 

クライアントは、モバイルやウェブ、当社のサーバー上の大きなファイル(GB以上)をアップロードしようとし、その後、多くの場合、アイドル状態の接続タイムアウトに直面しています。クライアントライブラリ、iOSなど、またはロードバランサから。

ファイルがクライアントによって実際にアップロードされているとき、接続が「アイドル」でないためにバイトが転送されているため、タイムアウトは発生しません。しかし、ファイルがNginxのバックエンド層に転送され、DjangoがファイルをS3にアップロードし始めると、アップロードが完了するまでクライアントとサーバーの間の接続がアイドル状態になると思います。

これが起こらないようにする方法はありますか、どのレイヤーでこの問題に取り組まなければなりませんか?

+0

NGINX confにclient_max_body_sizeを設定しましたか? –

+0

タイムアウトを発生させるのはどのシステムですか? ELBなど何か? ELBのデフォルトは60秒ですが、設定可能です。 –

+0

この場合、タイムアウトしているクライアントです –

答えて

1

アップロードハンドラを作成して、ファイルをs3に直接アップロードすることができます。このようにして、接続タイムアウトが発生することはありません。

https://docs.djangoproject.com/en/1.10/ref/files/uploads/#writing-custom-upload-handlers

私はいくつかのテストを行なったし、それが私の場合は完璧に動作します。

たとえば、botoを使用して新しいmultipart_uploadを開始し、チャンクを徐々に送信する必要があります。

チャンクサイズを検証することを忘れないでください。ファイルに1つ以上の部分が含まれている場合は、最小値は5MBです。 (S3の制限)

本当にs3に直接アップロードして接続タイムアウトを避けたい場合は、これはdjango-queued-storageの最良の代替手段だと思います。

ファイルを正しく管理し、もう一度送信しないように、独自のファイルフィールドを作成する必要があるでしょう。

次の例はS3BotoStorageです。

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 
3

私は同じ問題に直面し、django-queued-storageの上にdjango-storagesを使用して修正しました。 djangoが待っているのは、ファイルを受け取ったときにS3などのリモートストレージにアップロードするためのセロリタスクを作成し、誰かがファイルにアクセスしたときにS3でまだ利用できない場合は、ローカルから提供しますファイルシステム。このようにして、クライアントに応答を返すためにファイルをS3にアップロードするのを待つ必要はありません。

Load Balancerの背後にあるアプリケーションでは、上記の方法を使用するために、Amazon EFSなどの共有ファイル・システムを使用することができます。

1

ファイルをサーバーにアップロードするのをスキップして、それをs3に直接アップロードしてから、アプリケーションのURLのみを取得することができます。

それのためのアプリがあります:django-s3directあなたはそれを試すことができます。

関連する問題