Operación
Cómo se sabe si EQProxy está sano, qué se puede cambiar sin reiniciar y qué mirar cuando algo falla. Todo lo de esta página se apoya en contadores, líneas de log y comandos que el binario produce.
Comprobaciones de salud
Un backend sin [backend.*.health_check] considera operativos todos sus servidores. Con sección, las comprobaciones son activas y periódicas: type = "tcp" solo conecta; type = "http" hace un intercambio real y evalúa expect_status y, si está configurado, expect_rstring.
Un servidor cae tras fall comprobaciones fallidas consecutivas y vuelve tras rise éxitos. Los backup = true se comprueban también mientras no se usan, para que la primera petición tras fallar un primario no espere a una sonda. Con passive = true, el tráfico real —5xx y fallos de conexión— alimenta la misma máquina de estados.
Cuando no queda ningún servidor elegible: 503 en modo HTTP, cierre en modo TCP, y el contador no_available_server sube en ambos casos. Los estados drain y maintenance se ponen por la API; un drenaje termina cuando el último intercambio en vuelo suelta su guarda, y eso se registra una sola vez.
Métricas
GET /metrics en el plano de administración sirve texto Prometheus de cardinalidad acotada: 62 contadores sin etiquetas, más eqproxy_connections_active, eqproxy_config_reload_last_swap_seconds y eqproxy_build_info{version=…}.
| Contador | Qué mide |
|---|---|
eqproxy_http_requests_proxied_total | Intercambios HTTP servidos (h1, h2, h3, gRPC) |
eqproxy_connections_accepted_total, _rejected_total | Aceptadas y rechazadas por maxconn |
eqproxy_no_available_server_total | Peticiones sin servidor elegible |
eqproxy_connect_failures_total | Fallos al conectar con el backend |
eqproxy_header_timeouts_total | Timeouts de cabecera |
eqproxy_rate_limited_total, eqproxy_auth_denied_total | Rechazos por rate limit y por autenticación |
eqproxy_config_reloads_total, eqproxy_config_reload_failures_total | Recargas aplicadas y rechazadas |
eqproxy_backend_connections_reused_total | Conexiones reaprovechadas del pool |
eqproxy_cache_hits_total, _misses_total, _revalidations_total | Rendimiento de la caché |
eqproxy_map_reload_failures_total | Actualizaciones de mapa rechazadas |
eqproxy_access_log_dropped_total | Líneas de access log perdidas por contrapresión |
Con [metrics] detailed = true se añaden eqproxy_server_requests_total y eqproxy_server_errors_total (etiquetas backend, server) y eqproxy_route_requests_total (listener, route). max_series cuenta hojas entre las dos familias: una serie que no cabe se rechaza y se cuenta en eqproxy_metrics_detail_dropped_total, nunca se admite en silencio. Cuatro series sin etiquetas —_enabled, _series, _budget y _dropped_total— permiten distinguir «sin tráfico», «detalle apagado» y «el presupuesto lo rechazó».
Sin plano de administración también hay observabilidad: cada --metrics-interval (30 s por defecto) el proceso escribe METRICS con el snapshot de contadores en la salida de error, y una última línea METRICS … (final) al apagarse.
Logs
Hay dos flujos distintos.
- Log de proceso: va a la salida de error, con líneas
INFO,ERROR,METRICSyAUDIT. Ejemplo:INFO listener web listening on 0.0.0.0:8080. - Access log: opt-in con
[access_log]. Una línea por petición HTTP que llega al motor —cada flujo HTTP/2 y cada petición HTTP/3 cuentan como una—, en JSON o en la formacombinedde nginx.
{"time":"2026-10-25T03:31:17.518Z","listener":"web","client":"127.0.0.1:51632","protocol":"http/1.1","method":"GET","path":"/probe","host":"127.0.0.1:18181","status":200,"duration_ms":0,"backend":"api","server":"127.0.0.1:18180","user_agent":"curl/8.7.1","bytes":22}
La cadena de consulta nunca se registra. Una cabecera que no llegó a parsearse se registra con campos -; un intercambio que acabó antes de la respuesta lleva estado 0. El sumidero es un canal acotado de 4096 entradas con su propia tarea: si se llena o falla, se pierden líneas y sube access_log_dropped, pero nunca se retrasa una petición.
Recarga en caliente
| Qué | Cómo |
|---|---|
| Configuración completa | SIGHUP al proceso: relee el fichero, valida, resuelve y cambia de generación de forma atómica |
| Certificados TLS | POST /api/v1/certificates/reload o SIGHUP; los handshakes nuevos usan el certificado nuevo sin tocar las conexiones establecidas |
| Mapas de datos | Solos, cada reload_interval, sin recargar la configuración |
| Backends y listeners concretos | PUT /api/v1/backends/{name} y PUT /api/v1/listeners/{name} |
docker kill --signal=HUP eqproxy # el contenedor reenvía la señal
Un candidato que no valida no cambia nada: se registra el motivo, sube config_reload_failures y la generación anterior sigue sirviendo. Lo que no es recargable: el endpoint y los tokens del plano de administración, el destino y el formato del access log, max_series y, en un listener QUIC vivo, ALPN, client_ca y el rango de versiones TLS — un cambio ahí se rechaza y pide reinicio, nunca se aplica a medias.
Señales
| Señal | Efecto |
|---|---|
SIGINT | Apagado ordenado: deja de aceptar, drena lo vivo hasta shutdown_grace y sale con código 0 |
SIGHUP | Recarga en caliente de la configuración |
El binario instala manejadores para esas dos señales; no hay manejador propio para SIGTERM, así que su acción por defecto termina el proceso sin drenaje. Si necesitas apagado ordenado, envía SIGINT.
Límites y errores típicos
| Síntoma | Causa probable | Dónde mirar |
|---|---|---|
503 con no_available_server | Todos los servidores caídos, drenados o en su maxconn | GET /api/v1/status, eqproxy_no_available_server_total |
502 inmediato | connect_timeout o conexión rechazada por el backend | eqproxy_connect_failures_total |
504 | server_header_timeout o max_stream_duration | eqproxy_header_timeouts_total |
408 y cierre | El cliente no manda la cabecera a tiempo | client_header_timeout |
413 | limits.max_body_bytes superado | config explain |
429 | Rate limit de la regla | eqproxy_rate_limited_total |
400 en una petición válida para otro proxy | Framing ambiguo: EQProxy rechaza lo que no tiene una sola lectura | Log de proceso |
| Recarga que no cambia nada | Candidato inválido o 409 en la API | eqproxy_config_reload_failures_total y el log |
| Líneas de access log que faltan | Contrapresión del sumidero | eqproxy_access_log_dropped_total |
Checklist antes de dar un cambio por bueno
eqproxy config validate --config eqproxy.tomldevuelveconfig OK.eqproxy config diff actual.toml candidata.tomlse lee y no hay sorpresas.eqproxy doctor --config eqproxy.tomltermina endoctor: PASS.eqproxy config explainsobre las rutas críticas elige el backend esperado y los timeouts son los que crees.GET /api/v1/statusinforma la revisión nueva y los servidores en el estado correcto.- Los contadores de rechazo (
no_available_server,connect_failures,rate_limited) no se mueven tras el cambio.