2008-10-25 17 views
7

Una vez escribí un rastreador en .NET. Para mejorar su escalabilidad, traté de aprovechar la API asíncrona de .NET..NET NO tiene una comunicación de socket Asynchronouos confiable?

La System.Net.HttpWebRequest tiene la API asincrónica BeginGetResponse/EndGetResponse. Sin embargo, este par de API es solo para obtener un encabezado de respuesta HTTP y una instancia de Stream a partir de la cual podemos extraer el contenido de respuesta HTTP. Entonces, mi estrategia es usar BeginGetResponse/EndGetResponse para obtener de manera asincrónica el Stream de respuesta, luego usar BeginRead/EndRead para obtener asincrónicamente bytes de la instancia Stream de respuesta.

Todo parece perfecto hasta que el Crawler pase a la prueba de esfuerzo. En prueba de esfuerzo, el Crawler sufre un uso elevado de memoria. Comprobé la memoria con WinDbg + SoS y descubrí que muchas de las matrices de bytes son creadas por las instancias System.Threading.OverlappedData. Después de buscar en internet, encontré esta KB http://support.microsoft.com/kb/947862 de microsoft.

De acuerdo con la KB, el número de E/S asíncronas debe tener un "límite superior", pero no indica un valor límite "sugerido". Entonces, en mi opinión, esta KB no ayuda en nada. Esto es obviamente un error de .NET. Finalmente, tengo que descartar la idea de hacer bytes de extracción asíncrona de Stream de respuesta, y simplemente hacerlo de forma síncrona.

La biblioteca .NET que permite asíncrono IO con tomas red del punto (Socket.BeginSend/ Socket.BeginReceive/ NetworkStream.BeginRead/ NetworkStream.BeginWrite) debe tener un límite superior de la cantidad de buffers pendientes (ya sea enviar o recibir) con su IO asincrónico.

aplicación de la red debe tener una límite superior del número de excepcional S asíncrona que mensajes.

Editar: Agregar algunos signos de interrogación.

¿Alguien tiene alguna experiencia para hacer E/S asincrónicas en el zócalo & NetworkStream? Hablando en general, ¿el rastreador en producción hace E/S con internet con Synchronous o Asynchronosly?

+0

No hay un solo signo de interrogación, excepto en el asunto ... Una mala señal. –

Respuesta

3

Obviamente, desea limitar el número de solicitudes simultáneas, sin importar si su rastreador es de sincronización/asincronización. Ese límite no es fijo, depende de su hardware, red, ...

No estoy tan seguro de cuál es su pregunta aquí, ya que la implementación .NET de HTTP/Sockets está "bien". Hay algunos agujeros (consulte my post para controlar los tiempos de espera correctamente), pero hace el trabajo (tenemos un rastreador de producción que obtiene ~ cientos de páginas por segundo).

Por cierto, utilizamos IO sincrónico, solo por conveniencia. Cada tarea tiene un hilo, y limitamos el número de hilo simultáneo. Para la gestión de subprocesos, usamos Microsoft CCR.

+0

No tengo dudas de que las E/S síncronas en Socket funcionan bien en DotNet. Simplemente no confío en su API de E/S asíncrona. –

+0

El problema es abortar/cancelar operaciones, nunca funciona bien en .NET. Siempre debe preferir API de sincronización (con tiempos de espera), de esta manera no necesita cancelar la operación usted mismo. – ripper234

+0

También sugeriría incluir un WebRequest sincrónico en una Tarea. Además, no use Threads, pero las Tareas , que lo protegerán de una extensa Thread-Generation mediante el uso de Threadpool. Si usa adicionalmente un TaskCancelationSource, puede cancelar ejecutando fácilmente las tareas – spookycoder

10

Hmya, esto no es un problema de .NET Framework. El artículo KB enlazado podría haber sido un poco más explícito: "estás usando un arma cargada, esto es lo que sucede cuando apuntas a tu pie". Las viñetas de esa arma son .NET que le da la capacidad de iniciar tantas solicitudes de E/S asíncronas como se atreva. Hará lo que le pidas que haga, hasta que llegues a algún tipo de límite de recursos. En este caso, probablemente, tenga demasiados búferes de recepción anclados en el montón de generación 0.

La gestión de recursos sigue siendo nuestro trabajo, no de .NET. No es diferente de asignar memoria sin límite. Para resolver este problema en particular, debe limitar el número de solicitudes BeginGetResponse() incompletas. Tener cientos de ellos tiene poco sentido, cada uno de ellos tiene que pasar a través de Intertube uno a la vez. Agregar otra solicitud simplemente hará que tarde más en completarse. O bloquee su programa.

+0

. Pero, ¿cómo puedo decir el "límite superior" en mi programa? El hecho es que .NET no libera una matriz de bytes anclados, incluso si la aplicación ha abortado la operación BeginXXX después del tiempo de espera. Todavía creo que esto es un error de .net. –

+0

¿No puedes ver cómo esta es una respuesta útil? –

+1

Llamar a EndXxxx para liberar recursos es un requisito ** duro **. No te saltes eso. Claramente, es fácil saltarse por accidente cuando implementa un esquema de tiempo de espera. –

0

Ningún artículo de la base de conocimiento puede darle un límite superior. Los límites superiores pueden variar según el hardware disponible; lo que es un límite superior para una máquina de memoria 2G será diferente para una máquina con 16 g de ram. También dependerá del tamaño del montón del GC, qué tan fragmentado es, etc.

Lo que debe hacer es crear una métrica propia mediante el cálculo de la parte posterior de la envolvente. Calcule cuántas páginas desea descargar por minuto. Eso debería determinar cuántas solicitudes de sincronización quieres destacar (N).

Una vez que conoce N, cree un fragmento de código (como el extremo consumidor de una canalización productor-consumidor) que pueda crear N solicitudes de descarga asíncronas sobresalientes. Tan pronto como una solicitud finaliza (ya sea por tiempo de espera o debido al éxito), inicie otra solicitud asincrónica tirando de un elemento de trabajo de la cola.

También debe asegurarse de que la cola no crezca más allá de los límites, por ejemplo, por ejemplo, la descarga se vuelve lenta.

0

Esto ocurre cuando utiliza el método de envío asíncrono (BeginSend) de un socket. Si utiliza su propio grupo de hilos personalizado y envía los datos a través del hilo con el método de envío sincronizado, la mayoría de las veces resuelve este problema. Probado y probado.

3

Esto no está limitado a .Net.

Es un hecho simple que cada solicitud asíncrona (archivo, red, etc.) usa memoria y (en algún momento, para solicitudes de red) pool no paginado (ver here para detalles de los problemas que puede obtener en código no administrado) El número de solicitudes pendientes está, por lo tanto, limitado por la cantidad de memoria. Antes de la Vista había algunos límites de grupo no paginados muy bajos que le causarían problemas mucho antes de que se quedara sin memoria, pero en un entorno posterior a la vista las cosas son mucho mejores para el uso de grupo no paginado (consulte here).

Es un código administrado un poco más complejo, además de los problemas que se presentan en el mundo no administrado, también tiene que lidiar con el hecho de que los búferes de memoria que utiliza para las solicitudes asincrónicas se anclan hasta que se completen. Parece que estás teniendo estos problemas con las lecturas, pero es igual de malo, si no peor, para las escrituras (tan pronto como se activa el control de flujo TCP en una conexión, las terminaciones de envío van a empezar a tomar más tiempo y por eso esas memorias intermedias están fijados por más y más tiempo - vea here y here).

El problema no es que las cosas asincrónicas .Net estén rotas, sino que la abstracción es tal que hace que todo parezca mucho más fácil de lo que realmente es. Por ejemplo, para evitar el problema de fijación, asigne todos sus almacenamientos intermedios en un único bloque contiguo grande en el inicio del programa en lugar de en la demanda ...

Personalmente escribo tal rastreador en código no administrado, pero eso es solo yo;) Todavía enfrentarás muchos de los problemas, pero tienes un poco más de control sobre ellos.

Cuestiones relacionadas