asp.net core - net - ¿Kestrel está utilizando un solo hilo para procesar solicitudes como Node.js?
kestrel web server (2)
El enhebrado es específico del transporte.
Con el transporte libuv (el valor predeterminado en 2.0) como se indica en la respuesta de
Daniel JG
, hay una serie de bucles de eventos basados en la cantidad de procesadores lógicos en la máquina y eso se puede anular configurando el valor en las opciones.
Por defecto, cada conexión está vinculada a un hilo particular y todas las operaciones de E / S tienen lugar en ese hilo.
El código de usuario se ejecuta en subprocesos del grupo de subprocesos porque no confiamos en que los usuarios no bloqueen los subprocesos de E / S.
Cuando realiza llamadas de E / S en estos subprocesos de grupo de subprocesos (es decir,
HttpResponse.WriteAsync
), kestrel hace el trabajo de ordenar que regrese al subproceso de E / S apropiado al que estaba vinculado el socket.
Un flujo de solicitud típico se ve así:
[lectura de la red] despacho al grupo de subprocesos -> [análisis http], [ejecutar canalización de middleware] llamada para escribir -> poner en cola el trabajo del usuario en el subproceso IO [escribir en la red]
Por supuesto, siempre puede decirle a Kestrel que es un profesional y que nunca bloqueará el hilo IO y ejecutará su código en él. Pero no lo haría a menos que supiera lo que estaba haciendo (y no: D).
Tanto Kestrel como Node.js están basados en libuv .
Si bien Node.js afirma exactamente que usa un bucle de eventos , parece que no puedo encontrar si ese es el caso de Kestrel, o si utiliza la agrupación de hilos / cola de solicitud como IIS.
Cernícalo detrás de un servidor web
Node.js evento loop
┌───────────────────────┐
┌─>│ timers │
│ └──────────┬────────────┘
│ ┌──────────┴────────────┐
│ │ I/O callbacks │
│ └──────────┬────────────┘
│ ┌──────────┴────────────┐
│ │ idle, prepare │
│ └──────────┬────────────┘ ┌───────────────┐
│ ┌──────────┴────────────┐ │ incoming: │
│ │ poll │<─────┤ connections, │
│ └──────────┬────────────┘ │ data, etc. │
│ ┌──────────┴────────────┐ └───────────────┘
│ │ check │
│ └──────────┬────────────┘
│ ┌──────────┴────────────┐
└──┤ close callbacks │
└───────────────────────┘
Actualizado para ASP.Net Core 2.0
.
Como señaló poke, el servidor se ha dividido entre alojamiento y transporte, donde libuv pertenece a la capa de transporte.
El libuv
ThreadCount
se ha movido a sus propias
LibuvTransportOptions
y se configuran por separado en su generador de host web con el
UseLibuv()
ext:
-
Si
LibuvTransportOptions
claseLibuvTransportOptions
en github, verá una opciónThreadCount
:/// <summary> /// The number of libuv I/O threads used to process requests. /// </summary> /// <remarks> /// Defaults to half of <see cref="Environment.ProcessorCount" /> rounded down and clamped between 1 and 16. /// </remarks> public int ThreadCount { get; set; } = ProcessorThreadCount;
-
La opción se puede configurar en la llamada a
UseLibuv
, en su generador de alojamiento web. Por ejemplo:public static IWebHost BuildWebHost(string[] args) => WebHost.CreateDefaultBuilder(args) .UseLibuv(opts => opts.ThreadCount = 4) .UseStartup<Startup>() .Build();
Mientras que en ASP.NET Core 1.X, la configuración de Libuv era parte del servidor kestrel:
-
Si
KestrelServerOptions
claseKestrelServerOptions
en su repositorio github, verá que hay una opciónThreadCount
:/// <summary> /// The number of libuv I/O threads used to process requests. /// </summary> /// <remarks> /// Defaults to half of <see cref="Environment.ProcessorCount" /> rounded down and clamped between 1 and 16. /// </remarks> public int ThreadCount { get; set; } = ProcessorThreadCount;
-
La opción se puede configurar en la llamada a
UseKestrel
, por ejemplo, en una nueva aplicación ASP.Net Core:public static void Main(string[] args) { var host = new WebHostBuilder() .UseKestrel(opts => opts.ThreadCount = 4) .UseContentRoot(Directory.GetCurrentDirectory()) .UseIISIntegration() .UseStartup<Startup>() .Build(); host.Run(); }
Excavando a través del código fuente:
-
Puede ver los hilos de escucha de libuv (o
KestrelThreads
) que se crean enKestrelEngine
-
Algunos lugares llamarán a los métodos
ThreadPool
para que puedan ejecutar código en el conjunto de subprocesos CLR en lugar de los subprocesos libuv. (UsandoThreadPool.QueueUserWorkItem
). El grupo parece estar predeterminado con un máximo de 32K subprocesos que se pueden modificar a través de la configuración . -
El
Frame<TContext>
delega a la aplicación real (como una aplicación ASP.Net Core) para manejar la solicitud.
Entonces podríamos decir que usa múltiples bucles de eventos libuv para IO. El trabajo real se realiza en código administrado con subprocesos de trabajo estándar, utilizando el grupo de subprocesos CLR.
Me encantaría encontrar más documentación autorizada sobre esto ( Kestrel no dan muchos detalles). El mejor que he encontrado es Damian Edwards hablando de Kestrel en el canal 9 . Alrededor del minuto 12, explica:
- libuv usa un modelo de bucle de eventos de un solo hilo
- Kestrel admite múltiples bucles de eventos.
- Kestrel solo hace trabajo de IO en los bucles de eventos de libuv
- Todo el trabajo que no sea de E / S (incluido todo lo relacionado con HTTP, como análisis, encuadre, etc.) se realiza en código administrado en subprocesos de trabajo .net estándar.
Además, una búsqueda rápida ha devuelto: