2009-06-19 16 views
33

Tengo una aplicación escrita en .NET 3.5 que usa FTP para cargar/descargar archivos de un servidor. La aplicación funciona bien pero hay problemas de rendimiento:¿Cómo mejorar el rendimiento de FtpWebRequest?

  1. Lleva mucho tiempo hacer la conexión al servidor FTP. El servidor FTP está en una red diferente y tiene Windows 2003 Server (IIS FTP). Cuando se ponen en cola varios archivos para su carga, el cambio de un archivo a otro crea una nueva conexión usando FTPWebRequest y lleva mucho tiempo (alrededor de 8-10 segundos).

  2. ¿Se puede reutilizar la conexión? No estoy muy seguro sobre la propiedad KeepAlive. Qué conexiones se mantienen vivas y reutilizadas.

  3. El IIS-FTP en Windows Server 2003 no es compatible con SSL por lo que cualquiera puede ver fácilmente el nombre de usuario/contraseña a través de un detector de paquetes como WireShark. Descubrí que Windows Server 2008 admite SSL a través de FTP en su nueva versión si es IIS 7.0.

Básicamente quiero mejorar el rendimiento de carga y descarga de mi aplicación. Cualquier idea será apreciada.

** Tenga en cuenta que 3 no es un problema, pero me gustaría que la gente tenga comentarios sobre ella

+3

3 no es realmente un problema de rendimiento (aunque sigue siendo un problema); Sugeriría abordar eso por separado. –

Respuesta

2

personal he migrado todas nuestras aplicaciones lejos de usar FTP para el archivo de carga/descarga, y en lugar de hacer rodar una solución basada en XML Web Services en ASP.NET.

El rendimiento es mucho mejor, la seguridad es tan o tan poca como desee codificar (y puede usar las cosas integradas en .NET) y todo puede pasar por SSL sin problemas.

Nuestra tasa de éxito de obtener conexiones de nuestros clientes a través de sus propios servidores de seguridad es mucho mejor que ejecutar FTP.

0

KeepAlive está funcionando. FtpWebRequest almacena en caché las conexiones internas, por lo que se pueden reutilizar después de un tiempo. Para obtener detalles y una explicación de este mecanismo, puede consultar ServicePoint.

Otra buena fuente de información es buscar en la fuente FtpWebRequest (puede hacerlo en VS2008).

3

Definitivamente, deberías consultar BITS, que es una gran mejora con respecto a FTP. Las contraseñas de texto claro no son la única debilidad en FTP. También está la cuestión de predecir el puerto que se abrirá para una carga o descarga pasiva y solo una dificultad general cuando los clientes usan NAT o firewalls.

BITS funciona a través de HTTP/HTTPS utilizando extensiones IIS y admite cargas y descargas en cola que se pueden programar con baja prioridad. En general, es mucho más flexible que FTP si usa Windows en el cliente y el servidor.

BITS for PowerShell

BITS for .NET

17

No importa si las conexiones individuales toma mucho tiempo para conectar todo el tiempo que puede lanzar muchos en paralelo. Si tiene muchos elementos para transferir (digamos cientos), entonces tiene sentido lanzar decenas e incluso cientos de WebRequests en paralelo, utilizando los métodos asincrónicos como BeginGetRequestStream y BeginGetResponse.Trabajé en proyectos que enfrentaban problemas similares (tiempos de conexión/autentificación largos) pero al emitir muchas llamadas en paralelo, el rendimiento general era en realidad muy bueno.

También hace una gran diferencia si use the async methods o la sincrónica, tan pronto como tenga muchas (decenas, cientos) de solicitudes. Esto se aplica no solo a sus métodos de peticiones web, sino también a los métodos de Stream read/write que utilizará después de obtener la secuencia de carga/descarga. The Improving .Net Performance and Scalability libro está un poco desactualizado, pero gran parte de sus consejos sigue en pie, y es gratis para leer en línea.

Una cosa a tener en cuenta es que la clase ServicePointManager se encuentra al acecho en el Framwework con un único propósito: arruinar su rendimiento. Asegúrese de obtain the ServicePoint de su URL y cambie el ConnectionLimit a un valor razonable (al menos tan alto como la cantidad de solicitudes concurrentes que desee).

+0

¿Qué tiene que hacer el ServiceManager cuando estoy usando una sola conexión? Mi aplicación carga varios archivos uno por uno. ¿A qué debería configurar ConnectionLimit? También observé que lleva mucho tiempo establecer una conexión cuando una conexión ya está activa. En general, demora 4,5 segundos para que regrese el primer FTP GetRequestStream(). Luego, todas las conexiones posteriores tardan 1.3 segundos, pero si las conexiones se superponen, lleva 12 segundos crear una conexión. – A9S6

+1

Si una conexión ya está activa, la segunda no se conectará hasta que termine la primera, eso es lo que el ServiceManager controla (acelera la conexión en su nombre). Si está dedicado a usar una conexión y serializar todas las solicitudes, ServiceManager no hará ninguna diferencia. Mi punto era sobre hacer todas las solicitudes en paralelo. –

+0

Esta es una muy buena respuesta siempre que la secuencia en la que se recuperan los archivos no sea demasiado importante, las solicitudes se procesen en el orden en que se crearon, pero obviamente pueden finalizar en diferentes momentos dependiendo del tamaño del archivo. transferencias. Si requiere que ciertas transferencias finalicen antes que otras (por ejemplo, para controlar el orden en que se procesan los archivos recuperados), puede que este no sea el método ideal. –

0

AFAIK, cada FtpWebRequest tiene que configurar una nueva conexión, incluido el inicio de sesión en el servidor. Si desea acelerar las transferencias FTP, le recomendaría que use un cliente FTP alternativo en su lugar. Algunos de estos clientes alternativos pueden iniciar sesión y luego realizar múltiples acciones usando la misma conexión de comando.

Ejemplos de tales clientes incldue: http://www.codeproject.com/KB/IP/FtpClient.aspx que también incluye una buena explicación de por qué estas bibliotecas pueden operar más rápido que el estándar FtpWebRequest y http://www.codeproject.com/KB/macros/ftp_class_library.aspx que también parece una implementación bastante simple.

Personalmente, volqué mi propia implementación de FTP en .NET 1.1 días antes de que se introdujera FtpWebRequest y esto todavía funciona bien para .NET 2.0 en adelante.

+0

http://msdn.microsoft.com/en-us/library/system.net.ftpwebrequest.keepalive.aspx – TheXenocide

1

Recomiendo cambiar a rsync.
Pros:
Optimizado para reducir el tiempo de transferencia.
Soporta SSH para la transferencia segura
utiliza TCP manera hace que los chicos de TI departamento/firewall más felices

Contras:
Sin .NET soporte nativo
orienta hacia las instalaciones de servidores Linux - aunque hay puertos ventanas decente como DeltaCopy

en general, aunque se trata de una opción mucho mejor que FTP

+0

FTP también usa TCP – TheXenocide

5

depuración Red

Algunos trucos para la depuración de red sencilla:

  1. comprobar los tiempos de respuesta cuando hace ping al servidor FTP desde el servidor de aplicaciones.
  2. Compruebe los tiempos de respuesta para una ruta de rastreo (tracert desde un shell de DOS).
  3. Transfiera un archivo de la línea de comandos utilizando el comando ftp.
  4. Conéctese al servidor FTP a través de Telnet: telnet server 21.

Los resultados proporcionarán pistas para resolver el problema.

hardware de red

Para un trazado de ruta lenta:

  • Determinar por qué los dos equipos están teniendo problemas de red.
  • Actualice el hardware de red entre el enlace más lento.

configuración de red

Para un ping lenta:

  • Comprobar la configuración de red en cada máquina.
  • Asegúrese de que la configuración sea óptima.

Validar API

Una línea de comandos lenta sesión FTP le dirá que el problema no se limita a la API de FTP que está utilizando. No elimina la API como un problema potencial, pero ciertamente lo hace menos probable.

Errores de red

Si paquetes se están perdiendo entre el origen y el destino, mesa de ping le dirá. Es posible que deba aumentar el tamaño del paquete a 1500 bytes para ver cualquier error.

FTP Servidor de colas

Si no tiene ningún control sobre el servidor FTP de destino, tener un servidor intermediario recibir archivos subidos. El intermediario luego envía los archivos al servidor remoto a cualquier velocidad que pueda. Esto da la ilusión de que los archivos se envían rápidamente. Sin embargo, si los archivos deben existir en el servidor remoto tan pronto como se cargan, es posible que esta solución no sea viable.

software de servidor FTP

Utilice un demonio FTP diferente en el servidor FTP, como ProFTPd como Windows service. (ProFTPd posee complementos para diversas bases de datos que permiten la autenticación mediante consultas SQL.)

sistema operativo de servidor FTP sistema operativo basado en Unix Un

podría ser una opción mejor que una basada en Microsoft.

software del cliente FTP

Hay un número de diferentes APIs para enviar y recibir archivos a través de FTP. Podría tomar algún trabajo hacer que su aplicación sea lo suficientemente modular como para que simplemente pueda conectar un nuevo servicio de transferencia de archivos. Algunas API diferentes se enumeran como respuestas aquí.

Protocolo alternativo

Si FTP no es un requisito absoluto, tratar:

  • una red de Windows unidad
  • HTTPS
  • SCP, rsync, o programas similares (Cygwin puede haber requerido)
35

he hecho un poco de experimentación (subir unos 20 archivos en varios tamaños) en FtpWebRequest con los siguientes factores

KeepAlive = verdadero/falso

ftpRequest.KeepAlive = isKeepAlive; 

Connnection Grupo Nombre = UserDefined o nulo

ftpRequest.ConnectionGroupName = "MyGroupName"; 

conexión Límite = 2 (por defecto) o 4 o 8

ftpRequest.ServicePoint.ConnectionLimit = ConnectionLimit; 

modo = síncrono o asíncrono

ver this example

Mis resultados:

  1. Use ConnectionGroupName, KeepAlive = true took (21188.62 m) sec)

  2. Uso ConnectionGroupName, KeepAlive = false tomó (53449,00 mseg)

  3. No ConnectionGroupName, KeepAlive = false tomaron (40335,17 mseg)

  4. Uso ConnectionGroupName, KeepAlive = true; async = true, conexiones = 2 tomaron (11576,84 mseg)

  5. uso ConnectionGroupName, KeepAlive = true; asincrónicos = true, conexiones = 4 tomaron (10572,56 mseg)

  6. Use ConnectionGroupName, KeepAlive = true; async = true, connections = 8 took (10598.76 ms)

Conclusiones

  1. FtpWebRequest ha sido diseñado para soportar una agrupación de conexiones internas. Para garantizar que se utiliza el grupo de conexiones, debemos asegurarnos de que se esté configurando el ConnectionGroupName.

  2. Configurar una conexión es costoso. Si nos estamos conectando al mismo servidor ftp utilizando las mismas credenciales, tener el indicador mantener activo establecido en verdadero minimizará la cantidad de conexiones configuradas.

  3. Asincrónico es la manera recomendada si tiene muchos archivos para ftp.

  4. El número predeterminado de conexiones es 2. En mi entorno, un límite de conexión de 4 me dará la ganancia de rendimiento más general. Aumentar la cantidad de conexiones puede o no mejorar el rendimiento. Le recomendaría que tenga el límite de conexión como un parámetro de configuración para que pueda ajustar este parámetro en su entorno.

Espero que le resulte útil.

+4

Aunque estoy de acuerdo con la mayoría de sus comentarios, creo que su prueba es muy cuestionable. "21188.62 msec"? Probablemente quisiste decir "21" en su lugar. ¿Ejecutó esto una o más veces y promedió los resultados? Concluir que las conexiones = 4 da la mayor ganancia de una diferencia de 30 ms no es factible. Sin intención de ofender, pero la prueba tal como está no es útil, sino que es engañosa. - Si pudieras solucionar los problemas (precisión vs precisión, múltiples ejecuciones, más archivos, explicaciones de fondo, etc.) esta sería una gran publicación. – mafu

+2

Dudo que haya tecleado "21188.62 mseg" por accidente. Tal vez quisiste decir que "enumerar los tiempos en segundos sería más fácil de leer". Y no creo que la prueba no sea útil. Tal vez sacar conclusiones sobre un conjunto limitado de datos es desacertado, pero los datos están ahí si quieres usarlos. Por lo menos es un punto de partida. – d512

0

punto único consejo:

INFERIOR BUFFER/PEDAZO-tamaños a reducir significativamente el rendimiento

Motivo: Muchos más/S de disco, memoria i/o, ftp stream init y muchos más factores

1

Para resolver el problema de rendimiento, simplemente necesita establecer:

ftpRequest.ConnectionGroupName = "MyGroupName"; ftpRequest.KeepAlive = false; ftpRequest.ServicePoint.CloseConnectionGroup("MyGroupName");

1

Sé que es un hilo viejo, pero recientemente he tenido que pasar por una situación similar.

Necesitamos descargar más de 70 archivos XML de un servidor ftp en menos de 25 minutos sin abrir más de 5 conexiones durante ese período de tiempo.

Estas fueron todas las alternativas que probamos:

  • wget - Fue rápido, pero cada GET significó una nueva conexión. Tuvimos que parar debido a la cantidad de conexiones creadas. Tuvimos algunos problemas con GETM que están bien documentados en la web, por lo que no era una opción.
  • FtpWebRequest - Cada llamada a Crear registraría una nueva conexión, aunque usamos las propiedades KeepAlive y ConnectionGroupName. Además, fue muy lento.
  • Webclient - No verificamos si creó una nueva conexión para cada archivo (aunque supongo que sí), pero copió 1 archivo/minuto. Entonces no era una opción.

Terminamos usando la antigua secuencia de comandos ftp por lotes. Es rápido y solo uso una conexión para descargar todos los archivos. No es flexible, pero es mucho más rápido que todo lo demás que he probado (75 archivos en menos de 20 minutos).

-1

estaba trabajando unos días con eso ... y la velocidad era muy baja, nada que comparar con FileZilla ... finalmente lo resolví con multithreads. 10 hilos de hacer las conexiones para descargar me da una buena tasa, tal vez incluso podría mejorarse más .. con una configuración estandar ftprequest

PeticionFTP.ConnectionGroupName = "MyGroupName" 
PeticionFTP.ServicePoint.ConnectionLimit = 4 
PeticionFTP.ServicePoint.CloseConnectionGroup("MyGroupName") 

PeticionFTP.KeepAlive = False 
PeticionFTP.UsePassive = False 

PeticionFTP.UseBinary = True 

PeticionFTP.Credentials = New NetworkCredential(lHost.User, lHost.Password) 
-1

Prueba este código a continuación, obtendrá una mejor performence:

private void Upload144_Click(object sender, EventArgs e) 
{ 
    OpenFileDialog fileobj = new OpenFileDialog(); 
    fileobj.InitialDirectory = "C:\\"; 
    //fileobj.Filter = "Video files (*.mp4)"; 
    //fileobj.ShowDialog(); 

    if (fileobj.ShowDialog() == DialogResult.OK) 
    { 
     if (fileobj.CheckFileExists) 
     { 
      string test = Properties.Settings.Default.Connection; 
      SqlConnection con = new SqlConnection(test); 
      con.Open(); 
      string correctfilename = System.IO.Path.GetFileName(fileobj.FileName); 
      SqlCommand cmd = new SqlCommand("Insert into Path(ID,Path5) VALUES ((select isnull(MAX(id),0) + 1 from Path),'\\Videos\\" + correctfilename + "')", con); 

      cmd.ExecuteNonQuery(); 

      string path = Application.StartupPath.Substring(0, Application.StartupPath.Length - 10); 
      con.Close(); 

      //For Progressbar 
      DataTable dt = new DataTable(); 
     // SqlDataAdapter da = new SqlDataAdapter(cmd); 
     // da.Fill(dt); 

      timer5.Enabled = true; 

      // FOR FtpServer File Upload:: 
      string uploadfile = fileobj.FileName; 
      string uploadFileName = new FileInfo(uploadfile).Name; 

      string uploadUrl = "ftp://ftp.infotech.com/"; 
      FileStream fs = new FileStream(uploadfile, FileMode.Open, FileAccess.Read); 
      try 
      { 
       long FileSize = new FileInfo(uploadfile).Length; // File size of file being uploaded. 
       Byte[] buffer = new Byte[FileSize]; 

       fs.Read(buffer, 0, buffer.Length); 

       fs.Close(); 
       fs = null; 
       string ftpUrl = string.Format("{0}/{1}", uploadUrl, uploadFileName); 
       FtpWebRequest requestObj = FtpWebRequest.Create(ftpUrl) as FtpWebRequest; 
       requestObj.Method = WebRequestMethods.Ftp.UploadFile; 
       requestObj.Credentials = new NetworkCredential("[email protected]", "[email protected]"); 
       Stream requestStream = requestObj.GetRequestStream(); 
       requestStream.Write(buffer, 0, buffer.Length); 

       requestStream.Flush(); 
       requestObj = null; 
      } 
      catch (Exception ex) 
      { 
       //MessageBox.Show("File upload/transfer Failed.\r\nError Message:\r\n" + ex.Message, "Succeeded", MessageBoxButtons.OK, MessageBoxIcon.Information); 
      } 
     } 
    } 
} 
Cuestiones relacionadas