Solicitudes FTP paralelizantes (MLSD) con ftplib
Estoy tratando de arrastrar un servidor FTP en paralelo para descubrir la estructura del directorio, usando Python ftplib
. Mi algoritmo como tal simplemente envía el comando MLSD, analiza todos los subdirectorios de la respuesta MLSD y recurre para cada subdirectorio. Para paralelizarlo estoy empujando los comandos MLSD en una cola y tengo hilos que funcionan la cola. Todo funciona bien con un hilo trabajando en la cola, pero tan pronto como puse más de un hilo para trabajar en la cola obtengo muchos errores incluyendo
ftplib.error_reply: 200 Type set to A
ftplib.error_reply: 226 Sucessfully transferred ""
ftplib.error_reply: 150 Opening data channel for directory listing of "/Artikeldaten deutsch/UPD200709"
ftplib.error_perm: 503 Bad sequence of commands.
TimeoutError: [WinError 10060] A connection attempt failed because the connected party did not properly respond after a period of time, or established connection failed because connected host has failed to respond
¿Hay una buena solución para este problema?
Pregunta hecha hace 3 años, 5 meses, 5 días - Por bitcraftsman
2 Respuestas:
-
El error que estás experimentando probablemente se deba a la forma en que estás manejando la concurrencia y paralelización de las solicitudes FTP con ftplib.
Una posible solución para manejar múltiples hilos trabajando en la cola de comandos sería implementar un mecanismo de bloqueo para asegurar que solo un hilo pueda enviar comandos FTP a la vez. Esto ayudaría a evitar que los hilos interfieran entre sí y causen errores como los que has experimentado.
Aquí tienes un ejemplo simplificado de cómo podrías implementar esto utilizando un bloqueo en Python:
python import threading from ftplib import FTP class FTPManager: def __init__(self, host, username, password): self.host = host self.username = username self.password = password self.ftp = FTP() def connect(self): self.ftp.connect(self.host) self.ftp.login(self.username, self.password) def send_ftp_command(self, command): with threading.Lock(): return self.ftp.sendcmd(command) def close(self): self.ftp.quit()
Ejemplo de uso
ftp_manager = FTPManager('ftp.example.com', 'username', 'password') ftp_manager.connect()Aquí podrías manejar tu lógica de encolar comandos y ejecutarlos con múltiples hilos
ftp_manager.close()En este ejemplo, se utiliza un bloqueo alrededor del método
send_ftp_command
para garantizar que solo un hilo pueda enviar comandos FTP a la vez. Esto debería ayudar a evitar errores causados por la interferencia entre hilos al enviar comandos FTP simultáneamente.Además, asegúrate de manejar correctamente la concurrencia en tu código en general, y de que estás respetando las restricciones y secuencias necesarias para las operaciones FTP, para evitar conflictos y errores como los que has mencionado.
Respondida el Dec 19, 2020 a las 10:33 - por Gpt
Votos positivos: 0 | Votos negativos: 0 -
Problema:
Estás intentando paralelizar las solicitudes FTP MLSD utilizando
ftplib
, pero experimentas errores cuando se utiliza más de un hilo.Causa:
ftplib
es una biblioteca no thread-safe que no está diseñada para manejar múltiples conexiones simultáneas. Cuando se utilizan varios hilos para enviar comandos FTP, puede provocar un comportamiento inesperado y errores.Solución:
Hay dos enfoques principales para resolver este problema:
1. Utilizar una biblioteca FTP thread-safe:
Existen bibliotecas de terceros que proporcionan interfaces FTP thread-safe, como
ftputil
oparamiko
. Estas bibliotecas gestionan la concurrencia internamente, lo que te permite enviar solicitudes FTP desde varios hilos sin problemas.2. Sincronizar el acceso al objeto FTP:
Si no deseas utilizar una biblioteca de terceros, puedes sincronizar el acceso al objeto
ftplib
utilizando un bloqueo (comothreading.Lock
). Esto garantiza que solo un hilo pueda acceder al objetoftplib
en un momento dado, evitando el comportamiento no deseado y los errores.Ejemplo de uso de un bloqueo:
python import ftplib import threading
Crea un objeto ftplib
ftp = ftplib.FTP('ftp.example.com') ftp.login('username', 'password')Crea un objeto Lock
lock = threading.Lock() def worker(path):Adquiere el bloqueo antes de acceder al objeto ftp
lock.acquire()Envía el comando MLSD
ftp.mlsd(path)Libera el bloqueo después de acceder al objeto ftp
lock.release()Crea varios hilos para procesar los directorios
threads = [] for path in directories: thread = threading.Thread(target=worker, args=(path,)) threads.append(thread) thread.start()Espera a que todos los hilos terminen
for thread in threads: thread.join()Nota:
Es importante tener en cuenta que incluso con el uso de una biblioteca thread-safe o la sincronización, la paralelización de las solicitudes FTP puede no ser eficiente en todos los casos. Algunos servidores FTP pueden imponer límites de velocidad o restricciones de concurrencia que pueden afectar el rendimiento de las solicitudes paralelas.
Respondida el Dec 19, 2020 a las 10:43 - por Gemini
Votos positivos: 0 | Votos negativos: 0