如何提高FtpWebRequest的性能?
我有一个用.NET 3.5编写的应用程序,它使用FTP从服务器上传/下载文件。 该应用程序工作正常,但有性能问题:
-
连接到FTP服务器需要很长时间。 FTP服务器位于不同的networking上,并具有Windows 2003 Server(IIS FTP)。 当多个文件排队等待上传时,从一个文件到另一个文件的更改使用FTPWebRequest创build一个新的连接,并且花费大量时间(大约8-10秒)。
-
是否可以重新使用连接? 我不太确定KeepAlive属性。 哪些连接保持活着并被重用。
-
Windows Server 2003上的IIS-FTP不支持SSL,因此任何人都可以通过WireShark等数据包嗅探器轻松查看用户名/密码。 我发现Windows Server 2008在新版本中支持SSL over FTP(如果IIS 7.0)。
我基本上想要改善我的应用程序的上传/下载性能。 任何想法将不胜感激。
**请注意,3不是问题,但我希望人们对此有意见
我已经做了一些实验(上传约20个不同大小的文件)在FtpWebRequest与以下因素
KeepAlive = true / false
ftpRequest.KeepAlive = isKeepAlive;
连接组名称 = UserDefined或null
ftpRequest.ConnectionGroupName = "MyGroupName";
连接限制 = 2(默认)或4或8
ftpRequest.ServicePoint.ConnectionLimit = ConnectionLimit;
模式 =同步或asynchronous
看到这个例子
我的结果:
-
使用ConnectionGroupName,KeepAlive = true(21188.62毫秒)
-
使用ConnectionGroupName,KeepAlive = false(53449.00毫秒)
-
没有ConnectionGroupName,KeepAlive = false(40335.17毫秒)
-
使用ConnectionGroupName,KeepAlive = true; async = true,connections = 2(11576.84毫秒)
-
使用ConnectionGroupName,KeepAlive = true; async = true,connections = 4(10572.56毫秒)
-
使用ConnectionGroupName,KeepAlive = true; async = true,connections = 8(10598.76毫秒)
结论
-
FtpWebRequest
被devise为支持一个内部连接池。 为了确保使用连接池,我们必须确保ConnectionGroupName
正在被设置。 -
build立一个连接是昂贵的。 如果我们使用相同的证书连接到相同的ftp服务器,那么将keep alive标志设置为true将最大限度地减less连接设置的数量。
-
asynchronous是如果你有很多文件到FTP的build议方式。
-
默认的连接数是2.在我的环境中,连接数限制为4会给我带来最大的性能提升。 增加连接的数量可能会或可能不会提高性能。 我build议您将连接限制作为configuration参数,以便您可以在您的环境中调整此参数。
希望你会发现这有用。
只要可以并行启动多个连接,各个连接是否需要长时间连接并不重要。 如果你有很多东西需要传输(比如说数百个),那么使用BeginGetRequestStream和BeginGetResponse这样的asynchronous方法可以同时启动几十个甚至几百个WebRequest。 我曾经研究过面临类似问题的项目(长时间连接/validation时间),但同时发出很多调用,整体吞吐量实际上是非常好的。
如果您使用asynchronous方法或同步方法 ,只要您有很多(数十,数百)请求,它就会产生巨大的差异。 这不仅适用于WebRequests方法,还适用于在获取上载/下载stream之后使用的Stream 读 / 写方法。 “改进.Net性能和可伸缩性”这本书有些过时了,但是它的许多build议仍然存在,可以在线阅读。
需要考虑的一件事就是ServicePointManager类只能在一个唯一目的中潜伏在Framwework中:破坏你的性能。 确保获得 URL 的ServicePoint并将ConnectionLimit更改为合理的值(至less与您打算并发的请求数量一样多)。
debuggingnetworking
一些简单的networkingdebugging技巧:
- 检查从应用程序服务器ping FTP服务器的响应时间。
- 检查跟踪路由(来自DOS shell的
tracert
)的响应时间。 - 使用
ftp
命令从命令行传输文件。 - 通过Telnet连接到FTP服务器:
telnet server 21
。
结果将为解决问题提供线索。
networking硬件
对于慢速追踪路线:
- 确定两台计算机出现networking问题的原因。
- 在最慢的链接之间升级networking硬件。
networkingconfiguration
对于一个慢ping:
- 检查每台机器上的networkingconfiguration。
- 确保设置是最佳的。
validationAPI
一个缓慢的命令行FTP会话会告诉你,这个问题不是孤立的,你正在使用的FTP API。 它并没有消除API作为一个潜在的问题,但肯定使它不太可能。
networking错误
如果数据包在源和目标之间被丢弃,ping会告诉你。 您可能必须将数据包大小增加到1500字节才能看到任何错误。
FTP队列服务器
如果您无法控制目标FTP服务器,请让中间服务器接收上传的文件。 中介然后以任何速度将文件发送到远程服务器。 这给出了文件正在被快速发送的错觉。 但是,如果这些文件一旦上载就必须存在于远程服务器上,那么这种解决scheme可能不可行。
FTP服务器软件
在FTP服务器上使用不同的FTP守护进程,例如ProFTPd作为Windows服务 。 (ProFTPd具有允许使用SQL查询进行身份validation的各种数据库的插件。)
FTP服务器操作系统
基于Unix的操作系统可能比基于微软的操作系统更好。
FTP客户端软件
有许多不同的API通过FTP发送和接收文件。 可能需要一些工作来使您的应用程序足够模块化,以便您可以简单地插入新的文件传输服务。 这里列出了几个不同的API作为答案。
备用协议
如果FTP不是绝对的要求,请尝试:
- 一个Windowsnetworking驱动器
- HTTPS
- scp,rsync或类似的程序(可能需要Cygwin )
此链接描述ConnectionGroupName和KeepAlive影响: WebRequest ConnectionGroupName
你一定要看看BITS ,这对FTP来说是一个很大的改进。 明文密码不是FTP的唯一弱点。 还有一个问题就是预测端口将被打开以进行被动的上传或下载,而当客户端使用NAT或防火墙时,总体难度会更大。
BITS通过使用IIS扩展的HTTP / HTTPS进行工作,并支持排队的上传和下载,可以低优先级进行调度。 如果您在客户端和服务器上使用Windows,总体上它比FTP更灵活。
BITS for PowerShell
BITS for .NET
就我个人而言,我们已经将所有的应用程序都从FTP上传/下载,而不是使用基于ASP.NET Web服务的解决scheme。
性能得到了很大的提高,安全性与代码一样多(或者可以使用内置的.NET),它可以遍历SSL而不会出现任何问题。
我们通过自己的防火墙获得客户连接的成功率远远高于运行FTP。
看看这个网页 – http://www.ietf.org/rfc/rfc959.txt
它说在连接时使用不同的端口可以重用连接。
这有用吗?
我强烈build议用于.NET和Mono的Starksoft FTP / FTPS组件 。 它有一个可以caching和重用的连接对象。
我build议切换到rsync 。
优点:
优化减less传输时间。
支持SSH进行安全传输
使用TCP,使您的IT部门/防火墙家伙更快乐
缺点:
没有本地.NET支持
面向Linux服务器安装 – 尽pipe像DeltaCopy这样的体面的Windows端口
总的来说,虽然这是比FTP更好的select
我用EDT的ftp库取得了很好的效果:
http://www.enterprisedt.com/products/edtftpnet/overview.html
要解决性能问题,只需设置:
ftpRequest.ConnectionGroupName = "MyGroupName"; ftpRequest.KeepAlive = false; ftpRequest.ServicePoint.CloseConnectionGroup("MyGroupName");
我知道这是一个古老的线索,但我最近不得不经历类似的情况。
我们需要在25分钟内从ftp服务器下载70个以上的XML文件,而在此期间不能打开超过5个连接。
这些都是我们尝试的所有select:
- wget – 速度很快,但每个GET都意味着一个新的连接。 由于创build的连接数量不得不停止。 我们在GETM中遇到了一些问题,这些问题在networking上有很好的logging,所以这不是一个选项。
- FtpWebRequest – 即使我们使用了KeepAlive和ConnectionGroupName属性,每个Create调用都会logging一个新的连接。 再加上它非常缓慢。
- Webclient – 我们没有检查是否为每个文件创build了一个新的连接(虽然我假设它),但它复制了1个文件/分钟。 所以这不是一个select。
我们最终使用了老式的ftp批处理脚本。 这很快,我只用一个连接来下载所有的文件。 它不灵活,但比我尝试过的其他东西要快得多(在20分钟内75个文件)。
KeepAlive正在工作。 FtpWebRequestcaching里面的连接,所以可以在一段时间后重用。 有关此机制的详细信息和说明,请参阅ServicePoint 。
另一个很好的信息来源是查看FtpWebRequest来源(你可以在VS2008上做到这一点)。
AFAIK,每个FtpWebRequest必须build立一个新的连接 – 包括login到服务器。 如果你想加快FTP传输,我build议你使用一个备用的FTP客户端。 其中一些备用客户端可以login,然后使用相同的命令连接执行多个操作。
这样的客户端的例子包括: http : //www.codeproject.com/KB/IP/FtpClient.aspx其中还包括一个很好的解释,为什么这些库可以比标准的FtpWebRequest和http://www.codeproject更快的运行。 com / KB /macros/ ftp_class_library.aspx这看起来像一个足够简单的实现也。
就我个人而言,在FtpWebRequest被引入之前的1.1天,我把自己的FTP的实现推回到了.NET中,这对于.NET 2.0来说仍然适用。
单点build议:
缓冲器/缓冲器大小显着降低性能
原因:更多的磁盘I / O,内存I / O,FTPstreaminit和许多更多的因素
试试下面的代码,你会得到更好的性能:
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("test@sample.com", "test@123"); 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); } } } }
我工作了几天… …速度真的很低,没有什么可以与FileZilla比较…终于用multithreading解决了。 10个线程连接下载给我一个很好的速度,甚至可以改善更多..与标准ftprequestconfiguration
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)