You need to enable JavaScript to run this app.
导航
PyProton 使用最佳实践
最近更新时间:2024.08.30 17:42:17首次发布时间:2024.08.30 17:42:17

本文档主要介绍使用 PyProton 的最佳实践,内容将包含一些跟易用性、性能相关的使用技巧以及一些关于稳定性、健壮性的特别说明。

易用性篇

访问凭证获取与设置

PyProton 访问TOS需要经过TOS认证,目前支持两种认证类型:Assume Role、Access Key ID/Secret Access Key(简称AK/SK)。这些凭证的详细介绍请参考:Assume Role的获取文档Access Key(密钥)管理 。获取到这些凭证之后,PyProton支持两种方式设置它们:环境变量和代码设置,推荐设置到环境变量中。下面以AK/SK为例,介绍如何初始化 ProtonFileSystem :

from pyproton.protonfs import (
    ProtonFileSystem
)

fs = ProtonFileSystem(
    endpoint_url="http://tos-cn-beijing.volces.com",
    key="",            # access key id (ak)
    secret="",         # secret access key (sk)
    session_token="",  # 参考assume role的指引文档获取"SessionToken"
)
fs.ls("tos://bucket/to/path/", detail=False)

日志说明

整体上来说:用户看到的PyProton的日志是由:PyProton 和 Proton SDK的日志汇聚到一处所呈现出来的结果。以下分别说明:

  • PyProton的日志:是由PyProton自身通过Python代码产生。
  • Proton SDK的日志:是由Proton SDK借助于 log4j 日志框架通过Java代码产生,相关的日志配置目前暂不开放对外调整入口。

虽然以上两种类型的日志输出方式不同,但有一些共性:

  • 两者默认都汇聚到console。
  • 两者的log level都可统一通过一个环境变量控制,详情请见下一章节“Log Level设置”。

Log Level设置

PyProton 支持通过一个名为 PROTONFS_LOGGING_LEVEL 的环境变量来设置其自身和内置的Proton SDK的Log Level。默认情况下,如果用户没有设置该环境变量,PyProton会使用 INFO 级别。当用户想debug问题时,可以显式将其设置为DEBUG,环境变量的设置可以通过两种方式:

  • 直接在OS的shell中 export PROTONFS_LOGGING_LEVEL='DEBUG'生效。

  • 在计算引擎中间接指定,这里以最常用的Ray为例。
    方式一:

    import ray
    
    runtime_env = {"env_vars": {"PROTONFS_LOGGING_LEVEL": "DEBUG"}}
    
    ray.init(runtime_env=runtime_env)
    
    @ray.remote
    def f():
      # user business
    
    print(ray.get(f.remote()))
    

    方式二:

    import ray
    from ray.runtime_env import RuntimeEnv
    
    runtime_env = RuntimeEnv(env_vars={"PROTONFS_LOGGING_LEVEL": "DEBUG"})
    
    ray.init(runtime_env=runtime_env)
    
    @ray.remote
    def f():
      # user business
    
    print(ray.get(f.remote()))
    

JVM 内存调优

由于PyProton默认调用了Proton SDK的接口,因此无法避免拉起JVM。对于JVM的常规调优参数主要是Memory的最大(Xmx)、最小(Xms)阈值。PyProton已经默认设置了一批Proton在生产实践中久经考验且稳定的JVM参数。其中:Xms的默认值为64mXmx的默认值为2g,一般情况下用户无需额外调整,但如果面对极端场景,可以通过配置 PYPROTON_JVM_XMX 环境变量来调整Proton SDK的 Xmx 参数,配置方式请参考上文中的“Log Level设置”章节。

性能优化篇

ls API

在调用 ls API 的时候,如果只想获取一个 path 下的文件或文件夹清单,可显式将 detail 参数设置为False(默认值为True)以提升在性能。调用示例:

from pyproton.protonfs import (
    ProtonFileSystem
)

fs = ProtonFileSystem(
    endpoint_url="http://tos-cn-beijing.volces.com",
    key="",
    secret="",
    session_token="",
)
fs.ls("tos://bucket/to/path/", detail=False)

原因说明:为了对齐 fsspec#ls API的语义,当调用ls API的时候,如果 detail=True 当碰到一个资源类型为“目录”时,它会额外发起一次请求去获取该目录下所有资源汇总后的存储占用量。

rm API

在调用rm API的时候,如果想获得最佳的删除性能,请不要显式指定maxdepth参数并覆盖其默认值(该参数的默认值为:None)。调用示例:

from pyproton.protonfs import (
    ProtonFileSystem
)

fs = ProtonFileSystem(
    endpoint_url="http://tos-cn-beijing.volces.com",
    key="",
    secret="",
    session_token="",
)

# 是否需要将recursive设置为True,取决于是否需要递归删除子目录
fs.rm("tos://bucket/to/path/", recursive=True)

原因说明:如果用户显式设置 maxdepth 内部调用将会fallback到 fsspec#rm 的默认实现:对该 path 进行expand 和 walk(list)。对于删除海量文件场景,可能会引发超长的删除时间以及Proton SDK的OOM。
而如果不显式指定 maxdepth ,则默认会使用更高效的batch delete的删除方式。这种针对海量文件的删除优化,无论在稳定性和性能上都有大幅的提升。

File Operation: read/write API

write API

对于单个大文件(如GB级别)的写优化,以下使用实践将有助于降低写入耗时、提升写吞吐与稳定性:

  • 拆分chunk攒批多次写:建议chunk_size设置为“5MB”跟fsspec默认的buffer size保持一致,可以避免内存空间碎片,提升内存利用率。并且可以防止一次性积攒大量的数据因为内存OOM而造成稳定性问题。请参考以下示例代码:

    from pyproton.protonfs import (
        ProtonFileSystem
    )
    
    fs = ProtonFileSystem(
        endpoint_url="http://tos-cn-beijing.volces.com",
        key="",
        secret="",
        session_token="",
    )
    
    def write_in_chunks(file_object, content, chunk_size):
        for i in range(0, len(content), chunk_size):
            file_object.write(content[i : i + chunk_size])
    
    
    def write_via_pyproton(dest_folder, size_of_byte_per_file, file_name, content, chunk_size=0):
        full_file_path = f"{dest_folder}/{file_name}"
        if chunk_size != 0:
            with fs.open(full_file_path, "wb", block_size=chunk_size) as f:
                write_in_chunks(f, content, chunk_size)
        else:
            with fs.open(full_file_path, "wb") as f:
                f.write(content)
        assert fs.exists(full_file_path), f"File {full_file_path} does not exist."
    
  • 调优 Proton 参数以获得写时多盘Staging MPU的写优势从而答复提升写性能(降低写耗时),具体的参数说明请参考Proton官网文档[1],以下示例给出调优设置方式(请注意,所有配置项的值只支持字符串类型,如果值为数字,也请加上""):

    from pyproton.protonfs import (
        ProtonFileSystem
    )
    
    fs = ProtonFileSystem(
        endpoint_url="http://tos-cn-beijing.volces.com",
        key="",
        secret="",
        session_token="",
        # proton sdk支持的参数设置
        config_dict = {
            # other args...
            "fs.tos.multipart.staging-dir": "",
            "fs.tos.multipart.size": "",
            "fs.tos.multipart.thread-pool-size": "",
            "fs.tos.multipart.staging-buffer-size": "",
            "fs.tos.multipart.threshold": "",
            "fs.tos.task.thread-pool-size": "",
        }
    )
    

read API

对于单个大的二进制文件(如GB级别)的读优化,以下使用实践将有助于降低读耗时、提升读吞吐与稳定性:

  • 拆分chunk攒批多次读:建议chunk_size设置为“5MB”,可以防止一次性读取大量的数据因为内存OOM而造成稳定性问题。请参考以下示例代码:

    from pyproton.protonfs import (
        ProtonFileSystem
    )
    
    fs = ProtonFileSystem(
        endpoint_url="http://tos-cn-beijing.volces.com",
        key="",
        secret="",
        session_token="",
    )
    
    def read_from_chunks(file_handle, chunk_size=0):
        total_bytes_read = 0
        if chunk_size == 0:
            total_bytes_read = len(file_handle.read(-1))
        else:
            while True:
                data_chunk = file_handle.read(chunk_size)
                bytes_read = len(data_chunk)
                if not data_chunk:
                    break  # End of file reached
                total_bytes_read += bytes_read
    
        return total_bytes_read
        
    def read_via_pyproton(full_file_path, size_in_bytes, chunk_size=0):
        with fs.open(full_file_path, "rb") as f:
            assert read_from_chunks(f, chunk_size) == size_in_bytes
    
  • 对于有明确换行符且格式良好的文本文件,推荐使用 readlinereadlines API。

稳定性篇

关于连接稳定性

用户可以认为:在使用PyProton进行读写的过程中,无需担心连接稳定性的问题。用户在使用 PyProton 时,经常的模式可能会是“边读边处理”或“边写边处理”。“处理”的时间可长可短,如果在处理的耗时非常长的时候,用户也无需担心连接断开从而导致读写异常。因为 Proton 内置了连接重建和连接恢复以及断点续传和断点续读的一些接续处理逻辑。这些稳定性的优化对用户是无感的。