【问题标题】:Is boto3 client thread-safeboto3 客户端线程安全吗
【发布时间】:2019-03-20 03:09:20
【问题描述】:

S3 线程安全的 boto3 低级客户端?文档没有明确说明。

https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/s3.html#client

Github 中讨论了类似的问题

https://github.com/boto/botocore/issues/1246

但仍然没有维护者的回答。

【问题讨论】:

标签: python amazon-s3 aws-sdk boto3


【解决方案1】:

如果您查看 boto3 的 Multithreading/Processing 文档,您会发现他们建议每个会话使用一个客户端,因为实例之间存在共享数据,这些数据可以被各个线程更改。

对于这个确切的问题,似乎还有一个开放的 GitHub 问题。 https://github.com/boto/botocore/issues/1246

【讨论】:

  • 不幸的是,Multithreading/Processing 声明了资源实例。没有任何关于 Session 或低级 Client 的明确说明。
  • 文档中的示例使用了会话。在我看来,建议非常明确,您应该为每个线程使用不同的会话。
  • 感谢您的参考。最初的问题已经关闭,我已经写了一个带有链接的浓缩答案,它验证了你所说的:stackoverflow.com/a/70963911
【解决方案2】:

我最近尝试使用 concurrent.futures.ThreadPoolExecutor 使用单个 boto 客户端实例。我遇到了来自 boto 的异常。在这种情况下,我假设 boto 客户端不是线程安全的。

我遇到的异常

  File "xxx/python3.7/site-packages/boto3/session.py", line 263, in client
    aws_session_token=aws_session_token, config=config)
  File "xxx/python3.7/site-packages/botocore/session.py", line 827, in create_client
    endpoint_resolver = self._get_internal_component('endpoint_resolver')
  File "xxx/python3.7/site-packages/botocore/session.py", line 694, in _get_internal_component
    return self._internal_components.get_component(name)
  File "xxx/python3.7/site-packages/botocore/session.py", line 906, in get_component
    del self._deferred[name]

【讨论】:

  • 相同,缺少实际异常 :) KeyError: 'credential_provider'
  • 奇怪的是,当我在线程函数中初始化 boto3 客户端时,我得到了这个 credential_provider 异常,但是当它们都使用全局初始化的同一个客户端时,它可以工作
  • @LobsterMan 是的,但那一刻风向哪边吹?
【解决方案3】:

来自documentation

低级客户端是线程安全的。使用低级客户端时,建议实例化您的客户端,然后将该客户端对象传递给您的每个线程。

客户端的实例化不是线程安全的,而实例是线程安全的。为了使事情在多线程环境中工作,请将实例化放在全局 Lock 中,如下所示:

boto3_client_lock = threading.Lock()

def create_client():
    with boto3_client_lock:
        return boto3.client('s3', aws_access_key_id='your key id', aws_secret_access_key='your access key')

【讨论】:

  • 正确链接boto3.amazonaws.com/v1/documentation/api/latest/guide/…。来自文档:与资源和会话不同,客户端通常是线程安全的。不过,有一些注意事项(定义如下)需要注意。
  • 这实际上是正确的解决方案,其他解决方案根本不起作用(它们总是因 'credential_provider' 和/或 'endpoint_resolver' 而失败)。在将客户端传递给线程任务运行器之前,确实需要锁定客户端。
【解决方案4】:

您可以成功创建多个线程,但您必须为每个线程/进程实例化一个新会话,从而可以从 S3 存储桶异步下载。

以下示例:

import concurrent.futures
import boto3
import json


files = ["path-to-file.json", "path-to-file2.json"] 

def download_from_s3(file_path):
    # setup a new session
    sess = boto3.session.Session()
    client = sess.client("s3")
    # download a file
    obj = client.get_object(Bucket="<your-bucket>", Key=file_path)
    resp = json.loads(obj["Body"].read())
    return resp

with concurrent.futures.ThreadPoolExecutor() as executor:
     executor.map(download_from_s3, files)

【讨论】:

【解决方案5】:

boto 团队在May 19, 2021 上回答了这个问题。请参阅源文档here

资源实例不是线程安全的,不应跨线程或进程共享。这些特殊类包含无法共享的附加元数据。建议为每个线程或进程创建一个新的 Resource:

import boto3
import boto3.session
import threading

class MyTask(threading.Thread):
    def run(self):
        # Here we create a new session per thread
        session = boto3.session.Session()

        # Next, we create a resource client using our thread's session object
        s3 = session.resource('s3')

        # Put your thread-safe code here

在上面的示例中,每个线程都有自己的 Boto3 会话和自己的 S3 资源实例。这是一个好主意,因为资源在加载时包含共享数据,调用操作、访问属性或手动加载或重新加载资源都可以修改这些数据。

【讨论】:

    猜你喜欢
    • 2011-05-22
    • 1970-01-01
    • 2013-08-07
    • 1970-01-01
    • 1970-01-01
    • 2018-05-02
    • 1970-01-01
    • 1970-01-01
    • 2019-04-07
    相关资源
    最近更新 更多