我正在尝试创建一组FTP Web请求来下载文件集。
在单个线程中正常工作但我现在尝试使用多个线程但是我遇到超时异常。我想我错过了一些非常简单但似乎无法解决的问题
这是代码:
internal static void DownloadLogFiles(IEnumerable<string> ftpFileNames, string localLogsFolder)
{
BotFinder.DeleteAllFilesFromDirectory(localLogsFolder);
var ftpWebRequests = new Collection<FtpWebRequest>();
// Create web request for each log filename
foreach (var ftpWebRequest in ftpFileNames.Select(filename => (FtpWebRequest) WebRequest.Create(filename)))
{
ftpWebRequest.Credentials = new NetworkCredential(BotFinderSettings.FtpUserId, BotFinderSettings.FtpPassword);
ftpWebRequest.KeepAlive = false;
ftpWebRequest.UseBinary = true;
ftpWebRequest.CachePolicy = NoCachePolicy;
ftpWebRequest.Method = WebRequestMethods.Ftp.DownloadFile;
ftpWebRequests.Add(ftpWebRequest);
}
var threadDoneEvents = new ManualResetEvent[ftpWebRequests.Count];
for (var x = 0; x < ftpWebRequests.Count; x++)
{
var ftpWebRequest = ftpWebRequests[x];
threadDoneEvents[x] = new ManualResetEvent(false);
var threadedFtpDownloader = new ThreadedFtpDownloader(ftpWebRequest, threadDoneEvents[x]);
ThreadPool.QueueUserWorkItem(threadedFtpDownloader.PerformFtpRequest, localLogsFolder);
}
WaitHandle.WaitAll(threadDoneEvents);
}
class ThreadedFtpDownloader
{
private ManualResetEvent threadDoneEvent;
private readonly FtpWebRequest ftpWebRequest;
/// <summary>
///
/// </summary>
public ThreadedFtpDownloader(FtpWebRequest ftpWebRequest, ManualResetEvent threadDoneEvent)
{
this.threadDoneEvent = threadDoneEvent;
this.ftpWebRequest = ftpWebRequest;
}
/// <summary>
///
/// </summary>
/// <param name="localLogsFolder">
///
/// </param>
internal void PerformFtpRequest(object localLogsFolder)
{
try
{
// TIMEOUT IS HAPPENING ON LINE BELOW
using (var response = ftpWebRequest.GetResponse())
{
using (var responseStream = response.GetResponseStream())
{
const int length = 1024*10;
var buffer = new Byte[length];
var bytesRead = responseStream.Read(buffer, 0, length);
var logFileToCreate = string.Format("{0}{1}{2}", localLogsFolder,
ftpWebRequest.RequestUri.Segments[3].Replace("/", "-"),
ftpWebRequest.RequestUri.Segments[4]);
using (var writeStream = new FileStream(logFileToCreate, FileMode.OpenOrCreate))
{
while (bytesRead > 0)
{
writeStream.Write(buffer, 0, bytesRead);
bytesRead = responseStream.Read(buffer, 0, length);
}
}
}
}
threadDoneEvent.Set();
}
catch (Exception exception)
{
BotFinder.HandleExceptionAndExit(exception);
}
}
}
它似乎正在下载前两个文件(使用我假设的两个线程),但是当这些完成并且应用程序尝试移动到下一个文件时,似乎会发生超时。
我可以确认超时的FTPWebRequest是有效的,文件是否存在,我想我可能有一个开放的连接或什么的。
要发表评论但可能更容易在答案中阅读:
首先,如果我将ftpRequest.Timout属性设置为Timeout.Infinite,则超时问题会消失,但是无限超时可能不是最佳做法。所以我宁愿以另一种方式解决这个问题......
调试代码,我可以看到它到达:
ThreadPool.QueueUserWorkItem(threadedFtpDownloader.PerformFtpRequest, localLogsFolder);
它为每个FTP Web请求进入PerformFtpRequest方法并调用ftpWebRequest.GetResponse(),但之后只进一步前进两个请求。其余的请求保持活跃,但在前两个完成之前不再继续。所以这基本上意味着它们在开始之前等待其他请求完成时保持打开状态。
我认为这个问题的解决方案是允许所有请求一次执行(ConnectionLimit属性在这里没有效果),或者阻止执行调用GetResponse,直到它实际上准备好使用响应。
有关解决此问题的最佳方法的任何好主意吗?目前,我似乎只能想到的是我想避免的hacky解决方案:)
谢谢!
答案 0 :(得分:3)
您应该获取请求的ServicePoint并设置ConnectionLimit
ServicePoint sp = ftpRequest.ServicePoint;
sp.ConnectionLimit = 10;
默认的ConnectionLimit是2 - 这就是你看到这种行为的原因。
更新:请参阅此答案以获得更详尽的解释: