2

C#을 사용하여 1000 개의 원격 웹 페이지 (HttpWebRequest 사용)를 동시에 다운로드하고, 개별 로컬 파일에 기록하고 모든 파일을 다운로드 한 후 일부 처리 코드를 실행하는 가장 빠르고 안정적인 방법을 찾고 있습니다. 병렬 처리 및 비 차단 동시성을 최대한으로 활용할 수 있습니다.C#에서 동시 다운로드/처리

서버는 Windows 2008 및 .NET 4.0을 실행하는 쿼드 코어 (vCPU) VPS입니다 (최신 비동기/대기 프로그램을 사용할 수 없음).

무엇을 제안합니까?

업데이트 : 제안 된 옵션은 Reactive Extensions (Rx), Async CTP, TPL입니다.

비동기 CTP가 Rx 및 TPL 다음에 가장 적합한 방법이라고 생각됩니다. 뭐라구?

+0

WebClient가 작동합니다. –

+0

해당 페이지는 모두 단일 웹 사이트 또는 1000 개의 다른 사이트에서 제공됩니까? – svick

+0

@Ramhound WebClient가 기본적으로 멀티 스레딩을 지원한다고 생각하지 않습니다. – Nick

답변

1

VS2010 SP1은 .NET 4.0에서 Async CTP를 사용하여 async/await을 처리 할 수 ​​있습니다. VS2012 RC는 .NET 4.0에서 Async Targeting Pack을 사용하여 async/await을 처리 할 수 ​​있습니다.

그러나 실제로 async/await을 사용하지 않으려는 경우에도 작업 병렬 라이브러리는 .NET 4.0의 일부입니다.

0

나는 최근에 C# 5의 새로운 비동기 기능과 WebClient를 HttpWebRequest 대신 사용하여 비슷한 작업을 수행했습니다. DownloadDataTaskAsync와 같은 WebClient를 통해 멋진 비동기 메서드를 얻을 수 있습니다.

WebClient client = new WebClient(); 
byte[] data = await client.DownloadDataTaskAsync(url) 
+0

잘 알고 있지만 질문에 대답하지 않습니다. 특히 OP는 그가 비동기 -'와트 '를 사용할 수 없다고 말했기 때문에. – svick

4

나는 그 작업을 위해 Rx를 사용할 것이다.

string[] webpages = { "http://www.google.com", "http://www.spiegel.de"}; 

webpages 
    .Select(w => FetchWebPage(w)) 
    .ForkJoin() 
    .Subscribe(x => /*This runs when all webpages have been fetched*/ Console.WriteLine(x)); 

또는 svick 당신이 그것을 변경 될 수 있습니다 제안 당신은 동시에 최대 4 개 요청을 처리하기 위해 동시성을 제어 할 수 원한다면 : 또한 도우미 메서드를 neeed

Observable.ForkJoin(
    webpages 
     .Select(w => FetchWebPage(w)) 
     .Merge(4)) 
     .Subscribe(x => /*This runs when all webpages have been fetched*/ Console.WriteLine(x)); 

정규 비동기로 변환 수신 방법

public static IObservable<string> FetchWebPage(string address) 
{ 
    var client = new WebClient(); 

    return Observable.Create<string>(observer => 
    { 
     DownloadStringCompletedEventHandler handler = (sender, args) => 
     { 
      if (args.Cancelled) 
       observer.OnCompleted(); 
      else if(args.Error != null) 
       observer.OnError(args.Error); 
      else 
      { 
       observer.OnNext(args.Result); 
       observer.OnCompleted(); 
      } 
     }; 

     client.DownloadStringCompleted += handler; 

     try 
     { 
      client.DownloadStringAsync(new Uri(address)); 
     } 
     catch (Exception ex) 
     { 
      observer.OnError(ex); 
     } 

     return() => client.DownloadStringCompleted -= handler; 
    }); 
} 
+0

이것을 수정하여 병렬 처리 수준을 제한 할 수 있습니까? 왜냐하면 나는 동시에 1000 번 다운로드를 시작한다고 생각하지 않기 때문에 좋은 생각입니다. – svick

+0

내가 잘못 본 것이 아니라면 Rx는 내부적으로 TPL을 사용합니다. 또한 TPL은 Rx보다 비동기 작업을보다 직접 제어 할 수있는 것처럼 보입니다. – Nick

+0

@Nick : Rx는 고유 한 내장 함수 (스케줄러 등)를 사용합니다. TPL을 기반으로 만들어 졌다고는 생각하지 않습니다. –

1

에 대한 방법은 내가 비슷한 요구를했지만, 나를 위해 URL의 수는 7,000 이상 (약 25 걸리던 - 전체 28 개의 분)입니다. 내 솔루션의 경우 TPL을 사용했습니다. 각 URL에는 종속성이 없으므로 각 객체를 간단하게 객체에 캡슐화하고 컬렉션에 배치 한 다음 해당 컬렉션을 Parallel.ForEach() 호출에 전달하기가 쉽습니다.

각 다운로드가 완료되면 페이지의 내용을 살펴보고 발견 한 내용에 따라 추가 처리를 위해 보내드립니다.

내가 말했듯이 30 분이 넘는 시간이 소요되었지만 약 4.5 분만에 실행됩니다. (듀얼 쿼드 코어 제온 프로세서 @ 3GHz, Windows 7 Ultimate 64 비트 에디션 및 24GB의 RAM .... 지금은 많이 낭비되는 대용량입니다.

나는 마이크로 소프트의 TPL에 깊은 인상을 받았다. 나는 기존의 프로젝트/코드 대부분을 돌아보고 가능하면 TPL을 이용하도록 설계를 리팩토링했고, 나는 항상 새로운 코드에 대해 "TPL 치료"를 제공한다. 루프 반복 사이에 어떤 유형의 종속성이 있다면 항상 가능하지는 않습니다.

4

는 상관없이 기본으로 허용되는 최대 연결을 증가시킬 필요가 있음을 잊지 말고, 사용하게하는 비동기 방식 도메인 당 이 없습니다. 따라서 단일 도메인에 대해 많은 전화를 걸면 요금이 제한됩니다.

당신은 기본 설정 사용하여 독립형 (non-ASP.NET) 응용 프로그램에서이 문제를 해결 할 수 있습니다 : 당신이 ASP.NET에 있다면 기본 <processModel autoConfig="true" ...> 이후 예상대로이 작동하지 않습니다, 그러나

<system.net> 
    <connectionManagement> 
     <add address="*" maxconnections="200" /> 
    </connectionManagement> 
</system.net> 

을 속성을 사용하면 코어 당 12 개로 자동 구성되므로 총 2 개가 넘는 경우에도 여전히 필요에 맞지 않을 수 있습니다.

ServicePointManager.DefaultConnectionLimit = 200; 

가 참고 : 당신이로 사용할 수 있도록이 코드 기반의 접근 방식은 또한, non-ASP.NET 애플 리케이션을위한 동일하게 작동합니다 그래서 당신은 당신 위해 Application_Start 같은의 코드 기반의 접근 방식을 사용해야합니다 .config를 피하려면 "보편적 인"솔루션을 사용하십시오.