Multiprocesamiento de carga de FTP con un número preciso de conexiones

Por lo tanto, he podido usar el multiprocesamiento para cargar varios archivos a la vez en un servidor determinado con las siguientes dos funciones:

import ftplib,multiprocessing,subprocess def upload(t): server=locker.server,user=locker.user,password=locker.password,service=locker.service #These all just return strings representing the various fields I will need. ftp=ftplib.FTP(server) ftp.login(user=user,passwd=password,acct="") ftp.storbinary("STOR "+t.split('/')[-1], open(t,"rb")) ftp.close() # Doesn't seem to be necessary, same thing happens whether I close this or not def ftp_upload(t=files,server=locker.server,user=locker.user,password=locker.password,service=locker.service): parsed_targets=parse_it(t) ftp=ftplib.FTP(server) ftp.login(user=user,passwd=password,acct="") remote_files=ftp.nlst(".") ftp.close() files_already_on_server=[f for f in t if f.split("/")[-1] in remote_files] files_to_upload=[f for f in t if not f in files_already_on_server] connections_to_make=3 #The maximum connections allowed the the server is 5, and this error will pop up even if I use 1 pool=multiprocessing.Pool(processes=connections_to_make) pool.map(upload,files_to_upload) 

Mi problema es que (muy a menudo) termino recibiendo errores como:

 File "/usr/lib/python2.7/multiprocessing/pool.py", line 227, in map return self.map_async(func, iterable, chunksize).get() File "/usr/lib/python2.7/multiprocessing/pool.py", line 528, in get raise self._value ftplib.error_temp: 421 Too many connections (5) from this IP 

Nota: También hay un error de tiempo de espera que ocurre ocasionalmente, pero estoy esperando a que se vuelva hacia atrás. Es una cabeza fea otra vez, y en ese momento lo publicaré.

No obtengo este error cuando uso la línea de comando (es decir, “ftp -inv”, “open SERVER”, “user USERNAME PASSWORD”, “mput * .rar”), incluso cuando tengo (por ejemplo) 3 instancias de esto corriendo a la vez.

He leído la documentación de ftplib y multiprocesamiento, y no puedo averiguar qué es lo que está causando estos errores. Esto es algo así como un problema porque regularmente hago una copia de seguridad de una gran cantidad de datos y una gran cantidad de archivos.

  1. ¿Hay alguna forma de evitar estos errores o hay una forma diferente de hacer que el script / a haga esto?
  2. ¿Hay alguna manera de que le diga al script que si tiene este error, debería esperar un segundo y luego reanudar su trabajo?
  3. ¿Hay alguna forma de que el script cargue los archivos en el mismo orden en que están en la lista (por supuesto, las diferencias de velocidad significarían que no siempre serían 4 archivos consecutivos, pero en este momento el orden parece básicamente aleatorio)?
  4. ¿Alguien puede explicar por qué / cómo se hacen más conexiones a este servidor de manera simultánea de lo que requiere el script?

Entonces, solo manejar las excepciones parece estar funcionando (excepto por el error de recursión ocasional … aún no tengo ni idea de qué demonios está pasando allí).

Según el # 3, no estaba buscando que el orden estuviera al 100%, solo que la secuencia de comandos seleccionaría el siguiente archivo de la lista para cargar (por lo que las diferencias en la velocidad de los procesos podrían / ​​seguirían causando que la orden no sea completamente secuencial, habría menos variabilidad que en el sistema actual, que parece estar casi desordenado).

Podría intentar usar una única instancia de ftp por proceso:

 def init(*credentials): global ftp server, user, password, acct = credentials ftp = ftplib.FTP(server) ftp.login(user=user, passwd=password, acct=acct) def upload(path): with open(path, 'rb') as file: try: ftp.storbinary("STOR " + os.path.basename(path), file) except ftplib.error_temp as error: # handle temporary error return path, error else: return path, None def main(): # ... pool = multiprocessing.Pool(processes=connections_to_make, initializer=init, initargs=credentials) for path, error in pool.imap_unordered(upload, files_to_upload): if error is not None: print("failed to upload %s" % (path,)) 

respondiendo específicamente (2) ¿Hay alguna manera de decirle al script que si tiene este error, debe esperar un segundo y luego reanudar su trabajo?

Sí.

 ftplib.error_temp: 421 Too many connections (5) from this IP 

Esta es una excepción. Puedes atraparlo y manejarlo. Si bien Python no admite llamadas de cola, por lo que esta es una forma terrible, puede ser tan simple como esto:

 def upload(t): server=locker.server,user=locker.user,password=locker.password,service=locker.service #These all just return strings representing the various fields I will need. try: ftp=ftplib.FTP(server) ftp.login(user=user,passwd=password,acct="") ftp.storbinary("STOR "+t.split('/')[-1], open(t,"rb")) ftp.close() # Doesn't seem to be necessary, same thing happens whether I close this or not except ftplib.error_temp: ftp.close() sleep(2) upload(t) 

En cuanto a su pregunta (3) si eso es lo que quiere, haga la carga en serie, no en paralelo.

Espero que publique con una actualización con una respuesta a (4). Lo único que me viene a la mente es algún otro proceso con conexión ftp a esta IP.