【问题标题】:Efficient upload of large amount of images to Azure Storage in Python使用 Python 将大量图像高效上传到 Azure 存储
【发布时间】:2021-06-01 00:52:55
【问题描述】:

我需要找到上传大量图片(最多几千张)的最佳方式,平均每张图片的大小约为 6MB。我们的服务是用 Python 编写的。

我们有以下流程:

  1. 有一个服务创建了一个 BlobServiceClient。我们正在使用 CertificateCredentials 进行身份验证
  2. 服务在 Linux 上的容器中运行并用 Python 代码编写
  3. 服务正在接收一条消息,其中包含 6 到 9 个图像作为每个 Numpy ndarray + JSON 元数据对象
  4. 每次收到消息时,我们都会使用 ThreadPoolExecutor 将所有文件和 JSON 文件发送到存储,其中 max_threads = 20
  5. 我们没有使用库的异步版本

修剪和简化的代码将如下所示(下面将不起作用,只是一个说明,azurestorageclient 是 Azure Python SDK 的外包装。它有一个 BlobServiceClient 实例,我们用于创建容器和上传 Blob):

def _upload_file(self,
                 blob_name: str,
                 data: bytes,
                 blob_type: BlobType,
                 length=None):
    blob_client = self._upload_container.get_blob_client(blob_name)
    return blob_client.upload_blob(data, length=len(data), blob_type=BlobType.BlockBlob)

def _upload(self, executor: ThreadPoolExecutor, storage_client: AzureStorageClient,
                  image: ndarray, metadata: str) -> (Future, Future):

    DEFAULT_LOGGER.info(f"Uploading image blob: {img_blob_name} ...")
    img_upload_future = executor.submit(
       self.upload_file,
        blob_name=img_blob_name, byte_array=image.tobytes(),
        content_type="image/jpeg",
        overwrite=True,
    )

    DEFAULT_LOGGER.info(f"Uploading JSON blob: {metadata_blob_name} ...")
    metadata_upload_future = executor.submit(
        self.upload_file,
        blob_name=metadata_blob_name, byte_array=metadata_json_bytes,
        content_type="application/json",
        overwrite=True,
    )

    return img_upload_future, metadata_upload_future

def send(storage_client: AzureStorageClient, 
                image_data: Dict[metadata, ndarray]):

    with ThreadPoolExecutor(max_workers=_THREAD_SEND_MAX_WORKERS) as executor:
        upload_futures = {
            image_metadata: _upload(
                executor=executor,
                storage_client=storage_client,
                image=image,
                metadata=metadata
            )
            for metadata, image in image_data.items()
        }

当在信号强度波动很大的慢速网络中上传文件时,我们观察到此类服务的性能非常差。

我们现在正在尝试寻找和衡量如何提高性能的不同选项:

  1. 我们会先将文件存储到硬盘中,然后不时将它们以更大的块上传
  2. 我们认为上传单个大文件的性能应该更好(例如,将 100 个文件转换为 zip/tar 文件)
  3. 我们认为在连接不良时减少并行作业的数量也应该会更好
  4. 我们考虑使用 AzCopy 而不是 Python

关于如何在这种情况下工作,是否有任何其他建议或 Python 中不错的代码示例?或者也许我们应该更改用于上传数据的服务?例如使用 ssh 连接到 VM 并以这种方式上传文件(我怀疑它会更快,但得到了这样的建议)。

迈克

【问题讨论】:

    标签: python azure performance azure-storage


    【解决方案1】:

    根据你的情况,我建议你把一些文件压缩成一个大文件,然后分块上传。关于如何分块上传文件,可以使用BlobClient.stage_blockBlobClient.commit_block_list的方法来实现。

    例如

    block_list=[]
    chunk_size=1024
    with open('csvfile.csv','rb') as f:
       
       while True:
            read_data = f.read(chunk_size)
            if not read_data:
                break # done
            blk_id = str(uuid.uuid4())
            blob_client.stage_block(block_id=blk_id,data=read_data) 
            block_list.append(BlobBlock(block_id=blk_id))
            
    
    blob_client.commit_block_list(block_list)
    

    【讨论】:

    • 这是我们考虑的选项之一,但是,我们需要存储中的单个文件,因此我们需要触发一个后台进程,该进程将在那里解压缩并执行其他工作。我们还没有计算成本...
    • @Kokos 如果是这样,我认为您可以将每个文件分成小块上传,而不是将整个文件内容直接上传到 azure blob。此外,azcopy 是一个不错的选择。
    • 嗯,很有趣。我的想法正好相反(增加块大小,以便一次发送所有文件)。另外,我认为我们应该在糟糕的网络条件下减少并行性——同时连接太多。你怎么看?
    • @Kokos 可能有用。根据情况,azcopy是更好的选择。
    猜你喜欢
    • 2020-01-09
    • 1970-01-01
    • 2014-07-22
    • 2020-02-23
    • 2018-12-17
    • 1970-01-01
    • 2017-03-11
    • 2016-08-18
    • 2013-04-13
    相关资源
    最近更新 更多