【问题标题】:FileTransferClient upload file performanceFileTransferClient 上传文件性能
【发布时间】:2018-01-03 14:30:48
【问题描述】:

我的 SFTP 客户端出现性能问题。上传大文件(大约 700MB)需要很长时间并退出失败(发生延迟超时)。上传25MB 文件时会在一秒钟内完成。

这是实现ISFTPFile的函数:

def _create_target_from_source(target, source):
    def _read_chunks_from_file(filename, chunk_size=1024 * 64):
        with open(filename, 'rb') as fp:
            chunk = fp.read(chunk_size)

            while chunk:
                yield chunk
                chunk = fp.read(chunk_size)

    offset = 0
    for chunk in _read_chunks_from_file(source):
        d = target.writeChunk(offset, chunk)
        offset += len(chunk)

    return d

它的召唤:

@defer.inlineCallbacks
def _upload(client):

    mode_write = FXF_WRITE | FXF_CREAT | FXF_TRUNC
    client_file = client.openFile(target, mode_write, {})

    yield client_file.addCallback(_create_target_from_source, source)
    defer.returnValue(client)

我在Consumer/Producer 中找到了一些想法,但我不知道如何在我的示例中使用它们,或者它们是否甚至可以与FileTransferClient 一起使用。有什么方法可以加快我的客户端的上传速度吗?也许我的方法从一开始就错了?

第二个问题。有没有办法在 client.openFile 延迟触发之前开始上传?

更新:

说实话,我不确定我的代码是慢还是我的方法是错误的,因为我将代码更改为如下所示:

def _create_target_from_source(target, source):

    offsets_and_chunks = _read_chunks_with_offset_from_file(source)
    d = _async_map(target.writeChunk, offsets_and_chunks)

    return d

def _read_chunks_with_offset_from_file(filename, chunk_size):

    with open(filename, 'rb') as fp:
        chunk = fp.read(chunk_size)

        offset = 0
        while chunk:
            yield offset, chunk
            chunk = fp.read(chunk_size)
            offset += chunk_size

def _async_map(writeChunk, payload):

    try:
        offset, chunk = next(payload)

    except StopIteration:
        return defer.succeed(True)

    d = writeChunk(offset, chunk)  # this is sloooooooooooow
    d.addCallback(lambda _: _async_map(writeChunk, payload))

    return d

不能解决我的性能问题。例如,当我测试paramiko.SFTPClient 时,上传一个 700MB 的文件需要 33 秒。

我认为我在通过 SFTP 以扭曲方式上传文件时做错了,但我不确定到底是什么。

【问题讨论】:

  • _async_mappass 处理StopIteration,然后继续尝试writeChunk(offset, chunk),由于异常,offsetchunk 都不受约束。这只会在最后很重要,但也许这就是您的代码卡住的地方。如果解决这个问题没有帮助,我建议对代码进行检测,以便您可以看到它花费的时间。在它失败或你感到无聊之前它写了多少块?每次写入需要多长时间才能成功?在完成一次写入和开始下一次写入之间花费了多少时间?更多信息将有助于指导您的后续步骤。
  • 抱歉,我确实将 try/except 块返回 defer.succeed(True) 更改为处理 StopIteration。通过编辑修复。性能没有改变。我将尝试分析我的代码并发布结果。顺便说一句,还有其他方法可以通过 SSH 将文件发送到远程服务器吗?还是我的希望就此终结?
  • 你可以启动scp。 :) 我认为您已经找到了 Twisted 当前提供的最高级别的 SFTP API。如果您弄清楚如何让事情顺利进行,您可能会做出一些值得贡献的事情,以使其在未来变得更容易(并减少自己维护的代码)。就分析而言,我将从一些从正确位置小心发出的简单日志消息开始。使用 cprofile/etc 进行分析可能很有用,但不幸的是,很难理解基于 Twisted 的应用程序的配置文件。日志/打印通常要容易得多,并且通常会告诉您您想要什么。

标签: python twisted


【解决方案1】:

这段代码速度慢(并且容易出错)的原因是它会将整个文件读入内存并将所有要写入远程文件的块排队。

这就是发生的地方:

for chunk in _read_chunks_from_file(source):
    d = target.writeChunk(offset, chunk)
    offset += len(chunk)

循环一直运行 - 同步。您可以进行的一项更改是等待每个writeChunk 成功,然后再继续下一个。

有多种方法可以将同步循环转换为异步循环。您可以在每次迭代中使用 inlineCallbacks 和 yield d。或者你可以使用我在How refactor readChunk from SFTPFile to stop using inlineCallbacks? 的回答在没有inlineCallbacks 的情况下做类似的事情。

【讨论】:

    猜你喜欢
    • 2015-12-13
    • 1970-01-01
    • 2018-02-09
    • 2014-11-20
    • 1970-01-01
    • 2012-12-22
    • 1970-01-01
    • 1970-01-01
    • 2011-02-16
    相关资源
    最近更新 更多