c# - tipos - sistemas operativos para cluster
C#: cuándo usar subprocesos estándar, ThreadPool y TPL en un servidor de alta actividad (4)
Lo que más me confunde es el hecho de que en todas partes que leo, veo algo como "usar un subproceso creado manualmente (o grupo de subprocesos personalizados) para manejar tareas de" larga ejecución ", y uso TPL para tareas de corta duración o tareas que requiere procesamiento paralelo ".
Extraño consejo, o tal vez te citas mal un poco. Un subproceso también puede procesarse en paralelo, y con el TPL puede crear una tarea con la opción LongRunning. Lo que queda es que no debe iniciar tareas largas en ThreadPool.
¿Qué es exactamente una tarea de larga duración? ¿Son 5 segundos, 60 segundos, una hora?
El TPL se ejecuta sobre ThreadPool y el TP creará nuevos subprocesos a un máximo de 2 por segundo. Por lo tanto, el tiempo de ejecución es> = 500 ms.
Incluso con un hardware potente, ¿no existe la posibilidad de que pueda cargar una carga de trabajo demasiado alta en cualquier grupo de subprocesos / cola de elementos de trabajo que tengo?
Sí, ninguna herramienta de enhebrado puede ampliar su capacidad real ...
Con 20 mil clientes, probablemente necesitará una granja de servidores, una opción para incluir en su diseño temprano ...
Por lo tanto, probablemente debería darle un buen vistazo a WCF antes de profundizar en los enchufes.
Últimamente he estado leyendo mucho sobre subprocesos mientras busco desarrollar un servidor TCP escalable y de alto rendimiento capaz de manejar hasta 10,000-20,000 clientes, cada uno de los cuales se comunica de manera constante y bidireccional al servidor con un comando sistema. El servidor recibirá un comando y ejecutará una sola tarea (o varias) según el comando. Mi pregunta es cómo hacer uso apropiado de las construcciones de subprocesos de .NET para una variedad de situaciones, ejecutando tareas que pueden durar entre un minuto y varias horas, dependiendo del trabajo que se realice.
Lo que más me confunde es el hecho de que en todas partes que leo, veo algo como "usar un subproceso creado manualmente (o grupo de subprocesos personalizados) para manejar tareas de" larga ejecución ", y uso TPL para tareas de corta duración o tareas que requiere procesamiento paralelo ". ¿Qué es exactamente una tarea de larga duración? ¿Son 5 segundos, 60 segundos, una hora?
¿Con qué período de tiempo debo usar cada uno de estos tres métodos para crear hilos:
- Hilos creados manualmente
- La clase .NET ThreadPool
- TPL
Otro problema que he contemplado es el siguiente: digamos que mi servidor tiene 20,000 clientes conectados, cada uno de los cuales envía 1 comando (que podría traducirse en una o varias tareas) por segundo. Incluso con un hardware potente, ¿no existe la posibilidad de que pueda colocar una carga de trabajo demasiado alta en cualquier grupo de subprocesos / cola de elementos de trabajo que tengo, generando así una excepción OutOfMemoryException después de que la cola se llene lentamente al máximo?
Cualquier idea sería muy apreciada.
La sugerencia de Marc es la forma en que lo haría. Pero si las tareas duran más de un segundo y los clientes envían una solicitud por segundo, la cola aumentará constantemente.
En ese caso, usaría un servidor como fachada, que recibe todas las solicitudes de los clientes y les envía las respuestas de forma asíncrona.
El servidor pondría todas las solicitudes en una cola de mensajes que es leída por varios otros servidores. Esos servidores manejan las solicitudes y ponen la respuesta en otra cola de mensajes que lee el primer servidor.
Otra solución sería utilizar un servidor de equilibrio de carga.
Parece que estás construyendo un servidor que atenderá miles de solicitudes concurrentes, cada una de larga duración en términos de minutos a horas.
Por lo general, las cargas de trabajo de subprocesos son lo suficientemente cortas como para completar, como máximo, en unos pocos segundos. Algo más, comenzará a acaparar los recursos del servidor y afectará seriamente la escalabilidad de su servidor. Tener decenas de miles de subprocesos bloqueados en operaciones de larga ejecución, o realizar esas operaciones de larga duración simultáneamente, definitivamente matará su escalabilidad.
No estoy seguro de cuánto tiempo de CPU está consumiendo en cada larga ejecución. Esto afectará su diseño, por ejemplo:
Si cada ejecución prolongada se bloquea principalmente en la E / S, puede usar un subproceso para esperar en la E / S superpuesta o en el puerto de finalización de E / S, luego reactivar nuevos subprocesos para procesar la E / S completada (hasta un límite limitado) . Deberá tener un número límite de subprocesos para atender las conexiones en espera.
Si cada operación de larga duración espera a que se completen otras operaciones, considere Windows Workflow Foundation.
Si cada operación de larga duración consume CPU, no querrá que se ejecuten muchos de ellos al mismo tiempo, o de lo contrario, su servidor se derrumbará. En este caso, use MSMQ y / o TPL para poner en cola las tareas y asegúrese de que solo algunas se estén ejecutando al mismo tiempo.
En todo esto, parece que mantienes la conexión del cliente abierta. Lo peor que puedes hacer es mantener un bloqueo de hilo para cada conexión. Tendrá que implementar estrategias de agrupación de subprocesos para usar solo un número limitado de subprocesos para atender todas las conexiones pendientes.
En realidad, para ese escenario todos son secundarios; lo primero que debe mirar es asyc-IO, también .BeginRead(...)
como .BeginRead(...)
etc; esto le permite minimizar el número de subprocesos esperando en los puertos de finalización de E / S, mucho más eficientes.
Una vez que tenga un mensaje completo, a esa escala , arrojaría el mensaje a un grupo de subprocesos personalizado / cola sincronizada. Tendría un número controlado de subprocesos regulares (no subprocesos agrupados o IOCP) que atienden esa cola para procesar cada elemento.
En este momento estoy haciendo algo similar (escala inferior) en este momento; Para evitar que la memoria explote, he limitado la cola de trabajo; si se llena (es decir, los trabajadores no pueden mantenerse al día), entonces puede bloquear IOCP por un momento, tal vez con un tiempo de espera que finalmente le dice al cliente "demasiado ocupado" en la capa de IOCP.