Problema
En entornos de producción basados en Windows Server, es frecuente observar que la aplicación web desaparece de la red durante uno o dos minutos y luego vuelve a estar disponible sin intervención. Los monitores externos (Pingdom, Uptime Kuma) reportan socket timeout o unable to connect to server. Lo curioso es que los logs de IIS, del framework ASP.NET Core y de la base de datos aparecen vacíos o sin errores claros. El síntoma se repite de forma aleatoria: a veces bajo carga, a veces en periodos de baja actividad, y ocurre en diferentes regiones geográficas.
Este patrón – interrupciones breves, sin trazas evidentes y con errores de conexión a nivel de socket – es típico de fallos en la capa de transporte o en los componentes que gestionan la cola de peticiones (HTTP.sys, ThreadPool, conexiones a bases de datos o a clústers de búsqueda). La falta de información en los logs obliga a buscar señales en métricas de bajo nivel y a correlacionar varios subsistemas antes de llegar a la raíz.
Causa
Las causas más habituales que generan socket timeout intermitente en un stack Windows + IIS + ASP.NET Core son:
-
Agotamiento del ThreadPool o de la cola de solicitudes de HTTP.sys
Cuando el número de peticiones concurrentes supera la capacidad configurada, HTTP.sys empieza a rechazar conexiones y el cliente recibe un timeout. El problema puede aparecer aunque la CPU no esté al 100 % porque el bloqueo está en la gestión de sockets. -
Límites de conexiones simultáneas a recursos externos
- SQL Server: pool de conexiones agotado o tiempo de espera de la base de datos demasiado bajo.
- Elasticsearch: número máximo de conexiones HTTP o saturación del nodo.
Cuando una capa externa se vuelve lenta, los hilos de ASP.NET quedan bloqueados y el ThreadPool se llena.
-
Problemas de red internos
- Saturación de la interfaz de red del servidor (picos de tráfico, NIC en modo half‑duplex).
- Configuración de TCP/IP que provoca TIME_WAIT excesivo o limitaciones de MaxUserPort.
-
Configuración de IIS/HTTP.sys
- Cola de solicitudes (
QueueLength) demasiado pequeña. - Timeout de conexión (
connectionTimeout) o de petición (headerWaitTimeout) configurados con valores agresivos.
- Cola de solicitudes (
-
Bloqueos de código
Código síncrono o llamadas bloqueantes dentro de ASP.NET Core pueden mantener hilos ocupados durante varios segundos, reduciendo la disponibilidad del ThreadPool y provocando rechazos de nuevas conexiones. -
Reciclado inesperado del Application Pool
Un reciclado forzado (por memoria, tiempo o cambios en archivos) reinicia el proceso w3wp.exe, dejando un breve lapso sin respuestas.
Solución
Una estrategia reutilizable se basa en tres pilares: instrumentación temprana, correlación de métricas y pruebas de carga controlada.
1. Instrumentación de métricas de bajo nivel
| Métrica | Por qué es útil | Herramienta recomendada |
|---|---|---|
ThreadPool (threads disponibles / en uso) |
Detecta agotamiento antes de que los timeouts aparezcan. | Performance Counter \ASP.NET Applications\Thread Count o OpenTelemetry. |
Cola de HTTP.sys (CurrentQueueLength, RejectedRequests) |
Muestra si IIS está rechazando peticiones por saturación. | netsh http show servicestate o logman para exportar counters. |
Contadores de TCP (TcpConnections, TcpTimedWaitDelay) |
Identifica puertos en estado TIME_WAIT que pueden agotar el rango de puertos. | Get-NetTCPConnection en PowerShell. |
| Latencia y tasa de error de SQL/Elasticsearch | Correlaciona picos de latencia externa con los timeouts. | Exporters de Prometheus para SQL Server y Elastic. |
| Uso de CPU / Memoria del proceso w3wp.exe | Descarta cuellos de botella de hardware. | Process Explorer, PerfMon. |
2. Activar logs de HTTP.sys y de IIS
-
HTTPERR – registra errores de nivel socket antes de que lleguen a IIS.
reg add "HKLM\SYSTEM\CurrentControlSet\Services\HTTP\Parameters" /v EnableErrorLogging /t REG_DWORD /d 1 /f netsh http add iplisten ipaddress=0.0.0.0Reinicia el servicio HTTP para que el archivo
httperr.logempiece a recibir entradas. -
IIS Advanced Logging – habilita campos como
cs-uri-stem,sc-status,time-takenywin32-status. Configura un filtro para capturar solo códigos 0 (éxitos) y 10060 (timeout).
3. Correlación en tiempo real
Crea un dashboard en Grafana que combine:
- ThreadPool y QueueLength (líneas de tiempo).
- Latency de SQL y Elastic (histogramas).
- TCP connections (gauge).
- Eventos de HTTPERR (tabla).
Marca los eventos de timeout de los monitores externos y observa qué métricas presentan picos simultáneos.
4. Pruebas de carga controlada
Ejecuta una prueba de carga ligera (por ejemplo, k6 run script.js) que reproduzca el patrón de tráfico típico. Aumenta gradualmente la concurrencia hasta que aparezca el timeout. Durante la prueba, revisa los contadores para identificar el punto de quiebre. Esta fase permite validar hipótesis sin esperar a un incidente real.
5. Ajustes rápidos
- Incrementar
QueueLengthen el sitio IIS:appcmd set config "Default Web Site" -section:system.applicationHost/sites /[name='Default Web Site'].limits.queueLength:5000 /commit:apphost - Ajustar ThreadPool mediante
ThreadPool.SetMinThreadsal iniciar la aplicación (código C#). - Aumentar
MaxUserPorty reducirTcpTimedWaitDelaypara evitar agotamiento de puertos:netsh int ipv4 set dynamicport tcp start=10000 num=55535 reg add "HKLM\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters" /v TcpTimedWaitDelay /t REG_DWORD /d 30 /f - Revisar y optimizar llamadas bloqueantes: sustituir
ResultoWait()porawaitasíncrono siempre que sea posible.
6. Implementar OpenTelemetry
Despliega un agente que capture trazas distribuidas (request → SQL → Elasticsearch). Las trazas revelarán dónde se acumulan latencias y permitirán filtrar los requests que terminan en timeout.
Cuándo aplicar esta solución
- Síntomas: timeout de socket reportado por monitores externos, ausencia de errores en logs de aplicación/IIS, interrupciones breves y aleatorias.
- Entorno: Windows Server con IIS, aplicaciones ASP.NET Core, dependencias externas (bases de datos, clústers de búsqueda).
- No aplica: fallos persistentes con códigos HTTP 5xx claros, problemas de DNS, o cuando el servidor está completamente inactivo (p.ej., apagado o reinicio). En esos casos la investigación se centra en la capa de infraestructura o en la disponibilidad del host.
Código
# Habilitar logging de HTTPERR
reg add "HKLM\SYSTEM\CurrentControlSet\Services\HTTP\Parameters" /v EnableErrorLogging /t REG_DWORD /d 1 /f
net stop http
net start http
# Aumentar rango de puertos efímeros y reducir TIME_WAIT
netsh int ipv4 set dynamicport tcp start=10000 num=55535
reg add "HKLM\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters" /v TcpTimedWaitDelay /t REG_DWORD /d 30 /f
# Incrementar cola de IIS
appcmd set config "Default Web Site" -section:system.applicationHost/sites /[name='Default Web Site'].limits.queueLength:5000 /commit:apphost
Verificación
- Reproducir el timeout con la prueba de carga.
- Confirmar que los contadores de
ThreadPoolyQueueLengthmuestran valores cercanos al máximo justo antes del fallo. - Verificar que el archivo
httperr.logcontiene entradas conWin32Status=10060(timeout) en los momentos esperados. - Corregir los valores (p.ej., aumentar
QueueLength) y repetir la prueba. Si el timeout desaparece, la causa está confirmada. - Mantener el dashboard activo durante al menos 24 h para validar que los picos ya no coinciden con incidentes reales.
Notas adicionales
- En servidores con alta rotación de sockets, el rango de puertos efímeros puede agotarse rápidamente; ajustar
MaxUserPorta 65534 yTcpTimedWaitDelaya 30 s suele ser suficiente. - OpenTelemetry tiene bajo impacto de rendimiento, pero habilitarlo solo en producción puede generar gran volumen de datos; usa filtros por nivel de error o por endpoint crítico.
- Si después de los ajustes persisten los timeouts, considera introducir un reverse proxy ligero (NGINX o ARR) que ofrezca buffering y métricas de conexión adicionales sin cambiar la arquitectura principal.
- Documenta cualquier cambio de configuración en un control de versiones (por ejemplo, scripts PowerShell en Git) para que el proceso de rollback sea inmediato en caso de efectos colaterales.