FileTransferClient上传文件性能

时间:2018-01-03 14:30:48

标签: python twisted

我的SFTP客户端存在性能问题。上传大文件(aprox。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上传文件时做错了,但我不知道到底是什么。

1 个答案:

答案 0 :(得分:0)

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

以下是发生这种情况的地方:

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

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

有许多方法可以将同步循环转换为异步循环。您可以在每次迭代时使用inlineCallbacks并生成d。或者您可以使用How refactor readChunk from SFTPFile to stop using inlineCallbacks?的答案在没有inlineCallbacks的情况下执行类似的操作。