2008-10-25 2 views
7

한 번 .NET에 크롤러를 작성했습니다. 확장 성을 향상시키기 위해 .NET의 비동기 API를 활용하려고했습니다..NET에는 신뢰할 수있는 Asynchronouos 소켓 통신이 없습니까?

System.Net.HttpWebRequest에는 비동기 API 인 BeginGetResponse/EndGetResponse가 있습니다. 그러나이 API 쌍은 HTTP 응답 헤더와 스트림 인스턴스를 가져 와서 HTTP 응답 내용을 추출 할 수 있습니다. 그래서, 제 전략은 BeginGetResponse/EndGetResponse를 사용하여 비동기 적으로 응답 스트림을 얻은 다음 BeginRead/EndRead를 사용하여 비동기 적으로 응답 스트림 인스턴스에서 바이트를 가져 오는 것입니다.

크롤러가 스트레스 테스트를받을 때까지 모든 것이 완벽하게 보입니다. 스트레스 테스트에서 크롤러는 메모리 사용량이 높습니다. WinDbg + SoS를 사용하여 메모리를 검사하고 많은 바이트 배열이 System.Threading.OverlappedData 인스턴스에 의해 처리되는지 확인합니다. 인터넷에서 일부 검색을 한 후이 KB http://support.microsoft.com/kb/947862을 Microsoft에서 발견했습니다.

KB에 따르면 비동기 I/O의 수에는 "상한"이 있어야하지만 "제안 된"바운드 값은 지정되지 않습니다. 그래서, 내 눈에는이 KB가 아무 도움이되지 않습니다. 분명히 .NET 버그입니다. 마지막으로, 응답 스트림에서 비동기 추출 바이트를 수행하는 아이디어를 삭제하고 동기식으로 수행해야합니다.

점 순 소켓 비동기 IO를 허용하는 .NET 라이브러리는 (Socket.BeginSend/ Socket.BeginReceive/ NetworkStream.BeginRead/ NetworkStream.BeginWrite)의 양에 상한이 있어야합니다 버퍼 비동기 입출력을 가진 의 미결 (송신 또는 수신).

네트워크 응용 프로그램은 뛰어난 비동기 IO 그게 게시물의 수에 상한이 있어야합니다.

편집 : 몇 가지 물음표를 추가하십시오.

누구나 소켓상의 비동기 입출력을 수행 한 경험이 있습니까? & NetworkStream? 일반적으로 프로덕션 환경의 크롤러는 동기식 또는 비동기식으로 인터넷에서 I/O를 수행합니까?

+0

제목을 제외하고는 물음표가 아닙니다 ... 나쁜 징후입니다. –

답변

3

크롤러가 동기화/비동기 여부와 상관없이 동시 요청 수를 제한하려고합니다. 그 제한은 고정되지 않습니다, 그것은 당신의 하드웨어, 네트워크,에 달려 있습니다 ...

HTTP/소켓의 .NET 구현은 "확인"입니다. 몇 가지 구멍이 있습니다 (시간 제한을 올바르게 제어하는 ​​방법은 my post 참조).하지만 작업이 완료됩니다 (초당 수백 페이지를 가져 오는 프로덕션 크롤러가 있음).

지금까지 편의상 편의상 IO를 사용합니다. 모든 작업에는 스레드가 있으며 동시 스레드 수를 제한합니다. 스레드 관리의 경우 Microsoft CCR을 사용했습니다.

+0

DotNet에서 Socket상의 동기 입출력이 잘 작동한다는 것은 의심의 여지가 없습니다. 난 그냥 비동기 I/O API를 신뢰하지 않습니다. –

+0

문제는 ops를 중단하거나 취소하는 것이므로 .NET에서는 잘 작동하지 않습니다. 항상 API를 동기화 (시간 제한 포함)하는 것이 좋습니다. 이렇게하면 직접 API를 취소 할 필요가 없습니다. – ripper234

+0

작업에 동기 WebRequest를 래핑하는 것이 좋습니다. 또한 스레드를 사용하지 말고, 스레드 풀을 사용하여 광범위한 스레드 생성을 방지 할 수있는 작업 을 사용하십시오. TaskCancelationSource를 추가로 사용하면 쉽게 실행중인 작업을 취소 할 수 있습니다. – spookycoder

10

Hmya, 이것은 .NET Framework 문제가 아닙니다. 링크 된 KB 문서는 좀 더 명확 할 수 있습니다. "로드 된 총을 사용하고 있습니다. 이것이 발을 조준 할 때 발생하는 현상입니다." 이 총의 총알은 .NET이므로 많은 비동기 I/O 요청을 시작할 수 있습니다. 그것은 당신이 자원 제한의 일종을 치기 전까지 당신이 요구하는 것을 할 것입니다. 이 경우 아마도 세대 0 힙에 고정 된 수신 버퍼가 너무 많을 수 있습니다.

리소스 관리는 여전히 .NET의 것이 아니라 우리의 업무입니다. 바운드없이 메모리를 할당하는 것과 다르지 않습니다. 이 특정 문제를 해결하려면 완료되지 않은 BeginGetResponse() 요청 수에 제한을 두어야합니다. 수백 명의 사람들이 거의 이해하지 못한다면, 그들 모두는 한 번에 하나씩 인터 튜브를 통해 짜내 야합니다. 다른 요청을 추가하면 완료하는 데 시간이 오래 걸립니다. 또는 프로그램을 중단하십시오.

+0

하지만 내 프로그램에서 "상한"을 어떻게 알 수 있습니까? 사실은 응용 프로그램이 제한 시간 후 BeginXXX 작업을 중단하더라도 .NET에서 고정 된 바이트 배열을 해제하지 않는다는 것입니다. 나는 아직도 이것이 .net 버그라고 생각한다. –

+0

이것이 도움이되는 답변을 어떻게 볼 수 없습니까? –

+1

리소스를 해제하기 위해 EndXxxx를 호출하는 것은 ** 어려운 ** 요구 사항입니다. 그걸 건너 뛰지 마라. 시간 초과 체계를 구현할 때 우연히 건너 뛰기 쉽습니다. –

0

KB 문서는 상한값을 줄 수 없습니다. 상한선은 사용 가능한 하드웨어에 따라 달라질 수 있습니다. 2G 메모리 기계의 상한은 16g 램이있는 기계와 다를 수 있습니다. 또한 GC 힙의 크기, 조각화 방법 등에 따라 달라집니다.

봉투 계산식을 사용하여 자신의 메트릭을 산출해야합니다. 분당 다운로드 할 페이지 수를 계산하십시오. 그러면 미해결 (N)을 원하는 비동기 요청의 수를 결정해야합니다.

N을 알았 으면 N 개의 비동기 다운로드 요청을 생성 할 수있는 코드 (예 : 생산자 - 소비자 파이프 라인의 소비자 쪽)를 만듭니다. 요청이 완료되면 (시간 초과 또는 성공으로 인해) 대기열에서 작업 항목을 가져 와서 다른 비동기 요청을 시작합니다.

예를 들어 어떤 이유에서든 다운로드가 느려지는 경우 대기열이 경계를 넘어서지 않도록해야합니다.

0

소켓의 비동기 보내기 (BeginSend) 메소드를 사용할 때 이런 현상이 발생합니다. 사용자 정의 스레드 풀을 사용하고 동기화 된 보내기 메소드를 사용하여 스레드를 통해 데이터를 보내면 대부분이 문제가 해결됩니다. 테스트를 거쳐 입증되었습니다.

3

.Net에만 국한되지 않습니다.

각 비동기 요청 (파일, 네트워크 등)은 비 페이징 풀 (일부 지점에서는 네트워킹 요청 이상)을 사용한다는 단순한 사실입니다 (비 관리 코드에서 얻을 수있는 문제에 대한 자세한 내용은 here 참조).). 따라서 미해결 요청 수는 메모리 양에 의해 제한됩니다. Pre-Vista에는 메모리가 부족하기 전에 문제가 발생할 수있는 심각한 비 페이징 풀 제한이 있었지만 비스타 환경에서는 비 페이징 풀 사용의 경우 훨씬 더 좋습니다 (here 참조).

관리되지 않는 세상에서 얻는 문제 외에도 비동기 요청에 사용하는 메모리 버퍼가 해당 요청이 완료 될 때까지 고정되어 있기 때문에 관리 코드가 좀 더 복잡합니다. 읽기와 관련하여 이러한 문제가있는 것처럼 들리지만, 더 나쁜 것은 아니지만 쓰기 작업도 마찬가지입니다 (TCP 흐름 제어가 연결을 시작하자마자 전송 완료가 발생하는 데 더 오래 걸리기 시작하므로 해당 버퍼 더 길게 또는 더 길게 고정되어 있습니다. herehere을 참조하십시오.

문제는 닷넷 비동기적인 것들이 망가지는 것이 아니라, 추상화가 모든 것이 실제보다 훨씬 쉽게 보이게하는 것입니다. 예를 들어 피닝 문제를 피하려면 모든 버퍼를 프로그램 시작시 필요한 경우가 아닌 하나의 큰 연속 블록에 할당하십시오.

개인적으로 비공식 코드에 이러한 크롤러를 쓰겠습니다. 그냥 날;) 당신은 여전히 ​​많은 이슈에 직면 할 것이지만, 당신은 그들에 대해 좀더 통제권을가집니다.