2010-12-12 2 views
4

파일 컬렉션을 다운로드하기 위해 FTP 웹 요청 모음을 만들려고합니다.FTP 요청을 생성하는 여러 스레드가있는 ThreadPool이 시간 초과되었습니다.

단일 스레드에서이 작업이 올바르게 수행되었지만 지금은 여러 스레드로 작업하려고했지만 시간 초과 예외가 발생했습니다.

그것은 처음 두 파일을 다운로드하는 것으로 보인다
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가 유효하고 파일이 존재한다는 것을 확인할 수 있습니다. 연결이되어있을 수도 있습니다.


코멘트를 게시하려고했지만 아마 쉽게는 대답 읽기 : 나는 Timeout.Infinite에 ftpRequest.Timout 속성을 설정하면,

첫째, 시간 제한 문제는 그러나 무한 제한 시간을 가진 사라진다 아마 모범 사례가 아닙니다. 그래서 내가 볼 수있는 코드를 디버깅 ...

이 다른 방법으로 해결하는 이동하는 것을 선호 거라고는 얻을 때까지 :

ThreadPool.QueueUserWorkItem(threadedFtpDownloader.PerformFtpRequest, localLogsFolder); 

그것은 각 FTP 웹 요청에 대한 PerformFtpRequest 방법에 들어가 ftpWebRequest.GetResponse()를 호출하지만 처음 두 요청에 대해서만 계속 진행합니다. 나머지 요청은 계속 활성화되지만 처음 두 가지가 완료 될 때까지 더 이상 진행되지 않습니다. 따라서 기본적으로 시작하기 전에 다른 요청이 완료되기를 기다리는 동안 열어 둡니다.

이 문제에 대한 해결책은 모든 요청이 즉시 실행되도록 허용하는 것입니다 (ConnectionLimit 속성은 여기에 아무런 영향을 미치지 않습니다). 실제로 응답을 사용할 준비가 될 때까지 GetResponse를 호출하지 못하도록합니다.

이 문제를 해결하는 가장 좋은 방법은 무엇입니까? 내가 생각하고있는 것처럼 보일 수있는 모든 것은 내가 피하고 싶은 해피 솔루션이다.

고마워!

답변

3

당신은 요청에 대한 ServicePoint을 얻고 ConnectionLimit

ServicePoint sp = ftpRequest.ServicePoint; 
sp.ConnectionLimit = 10; 

기본 ConnectionLimit 2 인을 설정해야합니다 - 당신이 그 행동을보고있는 이유가있다.

UPDATE : 더 철저한 설명이 답변을 참조하십시오 :

How to improve the Performance of FtpWebRequest?

+0

감사 메이트. foreach 루프 내에서 FtpWebRequests를 작성했지만 동일한 문제가 발생하는 코드를 추가하려고했습니다. 연결 한계를 설정해야하는 곳입니까? – timothyclifford

+0

자세한 내용이 담긴 질문/답변 링크가 추가되었습니다. ServicePoint는 머리가 몇 번 튀어 나올 때까지 들어 본 적이없는 것들 중 하나입니다. –

관련 문제