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