gpt4 book ai didi

Python:通过 FTP 上传大量文件

转载 作者:太空宇宙 更新时间:2023-11-03 18:59:21 25 4
gpt4 key购买 nike

我正在开发一个 python 脚本,它监视一个目录(使用 libinotify)的新文件,并为每个新文件进行一些处理,然后将其复制到存储服务器。我们使用的是 NFS 挂载,但存在一些性能问题,现在我们正在使用 FTP 进行测试。看起来 FTP 使用的资源比 nfs 少得多(负载始终低于 2,而 nfs 则高于 5)。

我们现在遇到的问题是在 TIME_WAIT 状态下保持打开的连接数量。存储在时间等待中的峰值约为 15k 连接。

我想知道是否有某种方法可以重新使用以前的连接进行新的传输。

任何人都知道是否有某种方法可以做到这一点?

谢谢

最佳答案

这是一个新的答案,基于对前一个的评论。

我们将使用单个 TCP 套接字,并通过交替发送名称和内容来发送每个文件,如 netstrings ,对于每个文件,都在一个大流中。

我假设 Python 2.6,双方的文件系统使用相同的编码,并且您不需要大量并发客户端(但您可能偶尔需要两个客户端,例如,真正的客户端和测试仪) .我再次假设你有一个模块 filegenerator谁的generate()方法注册到 inotify 、排队通知和 yield一个接一个。

客户端.py:

import contextlib
import socket
import filegenerator

sock = socket.socket()
with contextlib.closing(sock):
sock.connect((HOST, 12345))
for filename in filegenerator.generate():
with open(filename, 'rb') as f:
contents = f.read()
buf = '{0}:{1},{2}:{3},'.format(len(filename), filename,
len(contents), contents)
sock.sendall(buf)

服务器.py:
import contextlib
import socket
import threading

def pairs(iterable):
return zip(*[iter(iterable)]*2)

def netstrings(conn):
buf = ''
while True:
newbuf = conn.recv(1536*1024)
if not newbuf:
return
buf += newbuf
while True:
colon = buf.find(':')
if colon == -1:
break
length = int(buf[:colon])
if len(buf) >= colon + length + 2:
if buf[colon+length+1] != ',':
raise ValueError('Not a netstring')
yield buf[colon+1:colon+length+1]
buf = buf[colon+length+2:]

def client(conn):
with contextlib.closing(conn):
for filename, contents in pairs(netstrings(conn)):
with open(filename, 'wb') as f:
f.write(contents)

sock = socket.socket()
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
with contextlib.closing(sock):
sock.bind(('0.0.0.0', 12345))
sock.listen(1)
while True:
conn, addr = sock.accept()
t = threading.Thread(target=client, args=[conn])
t.daemon = True
t.start()

如果您在 Windows 上需要超过 200 个客户端,在 linux 和 BSD(包括 Mac)上需要 100 个以上,在不太好的平台上需要十几个,您可能希望使用事件循环设计而不是线程设计,使用 epoll在 Linux 上, kqueue在 BSD 上,以及在 Windows 上的 IO 完成端口。这很痛苦,但幸运的是,有一些框架可以为您解决所有问题。 Twisted 是两个流行的(并且非常不同的)选择。和 gevent .

关于 gevent 的一件好事尤其是您现在可以编写线程代码,并通过一些简单的更改将其变成基于事件的代码,就像魔术一样。

另一方面,如果您最终想要基于事件的代码,最好从一开始就学习和使用框架,这样您就不必处理 accept 的所有繁琐部分。循环播放 recv直到您收到完整的消息并干净地关闭等等,然后只写您关心的部分。毕竟,上面一半以上的代码基本上是每个服务器共享的东西的样板,所以如果你不必编写它,为什么还要麻烦呢?

在评论中,你说:

Also the files are binary, so it's possible that I'll have problems if client encodings are diferent from server's.



请注意,我以二进制模式( 'rb''wb' )打开每个文件,并有意选择了可以处理二进制字符串而不试图将它们解释为字符或将嵌入的 NUL 字符视为 EOF 或类似内容的协议(protocol)(网络字符串) .而且,当我使用 str.format , 在 Python 2.x 中,除非你提供它,否则不会进行任何隐式编码 unicode字符串或给它基于语言环境的格式类型,我都没有这样做。 (请注意,在 3.x 中,您需要使用 bytes 而不是 str,这会更改一些代码。)

换句话说,客户端和服务器编码不会进入它;您正在执行与 FTP 的 I 模式完全相同的二进制传输。

但是,如果您想要相反的情况,为目标系统自动传输文本和重新编码怎么办?有三种简单的方法可以做到这一点:
  • 发送客户端的编码(在顶部一次,或每个文件一次),然后在服务器上,从客户端解码并重新编码到本地文件。
  • 以文本/Unicode 模式执行所有操作,甚至是套接字。这很愚蠢,在 2.x 中也很难做到。
  • 定义一种有线编码,比如 UTF-8。客户端负责将文件解码并编码为UTF-8进行发送;服务器负责在接收和编码文件上解码 UTF-8。

  • 使用第三个选项,假设文件将采用您的默认文件系统编码,更改后的客户端代码为:
    with io.open(filename, 'r', encoding=sys.getfilesystemencoding()) as f:
    contents = f.read().encode('utf-8')

    在服务器上:
    with io.open(filename, 'w', encoding=sys.getfilesystemencoding()) as f:
    f.write(contents.decode('utf-8'))
    io.open默认情况下,函数也使用通用换行符,因此客户端会将任何内容转换为 Unix 样式的换行符,而服务器将转换为其自己的 native 换行符类型。

    请注意,FTP 的 T 模式实际上不进行任何重新编码;它只进行换行转换(以及更有限的版本)。

    关于Python:通过 FTP 上传大量文件,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/16405298/

    25 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com