Usa un solo httpx.AsyncClient para un grupo de peticiones, establece su proxy= de forma explícita y cierra cada respuesta en streaming cuando termine su trabajo. Si el error es PoolTimeout, inspecciona primero quién posee las conexiones del cliente: esa excepción significa que una petición no pudo obtener una conexión dentro de su límite de espera del pool. Por sí sola, no demuestra que el proxy haya fallado. Soporte async de HTTPX, fases del timeout.
Esta guía incluye un ejemplo ejecutable y una demostración local de fallo. La demostración mantiene dos respuestas abiertas, observa cómo una tercera petición falla antes de llegar al proxy, luego cierra una respuesta y verifica que otra petición tiene éxito. Eso te da una forma concreta de separar la propiedad de las conexiones de los problemas de la red remota.
Empieza con un cliente explícito
Los ejemplos requieren Python 3.11 o posterior y fijan HTTPX 0.28.1. La ejecución registrada usó Python 3.14.7 y HTTPcore 1.0.9; la descarga contiene las versiones fijadas de todas las dependencias. HTTPX 0.28 eliminó el antiguo argumento proxies=: usa el singular proxy= para esta configuración. Changelog de HTTPX etiquetado.
La fábrica de clientes reutilizable de la descarga es:
import httpx
def make_client(proxy_url: str) -> httpx.AsyncClient:
return httpx.AsyncClient(
proxy=proxy_url,
trust_env=False,
http2=False,
follow_redirects=False,
limits=httpx.Limits(max_connections=2, max_keepalive_connections=2),
timeout=httpx.Timeout(connect=5.0, read=5.0, write=5.0, pool=0.25),
)El pequeño pool de dos conexiones hace que este ejercicio sea fácil de inspeccionar; no es una capacidad de producción recomendada. El límite de conexiones y el límite de keep-alive de HTTPX controlan cosas distintas. max_connections acota las conexiones del pool, mientras que max_keepalive_connections acota las conexiones inactivas que se conservan. Un límite de conexiones tampoco acota cuántas tareas de aplicación creas. Límites de recursos de HTTPX.
El ejemplo completo async_proxy_check.py envía exactamente tres GET a través de un solo cliente. Un semáforo admite como máximo dos a la vez. Cada petición tiene un plazo de operación de diez segundos que empieza antes de esa espera de admisión, además de los timeouts del cliente indicados arriba. Las respuestas se leen dentro de async with client.stream(...), y la lectura se detiene con BodyTooLarge si el cuerpo decodificado supera los 65.536 bytes. El tope de bytes limita el contenido de respuesta aceptado; no es un límite estricto de la memoria del descompresor. Son límites deliberados para un diagnóstico pequeño, no un consejo de dimensionamiento de cargas de trabajo.
No hay bucle de reintentos automático. Conserva el mismo cliente para el trabajo que le pertenece en lugar de crear un cliente nuevo dentro de cada tarea de petición. Los streams gestionados por contexto se cierran al salir; las llamadas manuales a client.send(..., stream=True) hacen que cerrar la respuesta sea responsabilidad tuya. Ciclo de vida del cliente y del stream en HTTPX.
Mantén la conexión al proxy separada del destino
Un destino HTTPS no requiere automáticamente una dirección de proxy https://. Con un proxy HTTP, el cliente puede solicitar un túnel CONNECT y luego negociar TLS con el destino HTTPS a través de ese túnel. Usa el esquema de proxy y el método de autenticación que documente tu proveedor. El experimento local de abajo ejercita el reenvío HTTP plano, no CONNECT, TLS, SOCKS, autenticación ni el servicio de un proveedor. Configuración de proxy en HTTPX.
Para esta línea base, proxy= selecciona la ruta y trust_env=False mantiene la configuración heredada del entorno fuera del cliente. En HTTPX 0.28.1, no esperes que NO_PROXY anule un proxy= proporcionado de forma explícita. Si necesitas excepciones de ruta, configúralas y verifícalas deliberadamente en lugar de asumir que el entorno hizo que este cliente las evitara. Enrutamiento del cliente etiquetado.
También hay una consecuencia para los certificados: trust_env=False desactiva el uso que HTTPX hace de SSL_CERT_FILE y SSL_CERT_DIR. El ejemplo mantiene activada la verificación normal de certificados. Si tu despliegue requiere una CA privada, configura un SSLContext de confianza explícito como se describe en la guía de SSL de HTTPX; no arregles un error de confianza con verify=False. Consulta también las variables de entorno de HTTPX.
Reproduce el fallo del pool sin una cuenta de proxy
Descarga y extrae el ejemplo de proxy async con HTTPX. Desde el directorio extraído, ejecuta:
python3 -m venv .venv
. .venv/bin/activate
python -m pip install -r requirements.txt
python pool_timeout_demo.pyLa instalación de dependencias usa el índice de paquetes. La demostración en sí crea un origen HTTP y un proxy de reenvío temporales en loopback y no envía ninguna petición a un destino externo. Usa la misma fábrica de clientes mostrada arriba, con HTTP/1.1 y un pool de dos conexiones.
El resultado importante es la secuencia de peticiones, no una puntuación de velocidad:
| Paso | Observación del cliente | Observación del proxy y del origen |
|---|---|---|
Abrir /hold/1 y /hold/2 como streams manuales | Ambos cuerpos de respuesta quedan sin terminar | Ambas rutas han llegado |
Solicitar /blocked con el pool lleno | PoolTimeout | /blocked no ha llegado a ninguno de los dos saltos |
Llamar a aclose() en la primera respuesta retenida | Se libera una conexión ocupada | La segunda respuesta sigue deliberadamente abierta |
Solicitar /ok | HTTP 200 con el cuerpo JSON esperado | /ok llega a ambos saltos |
La ejecución registrada produjo exactamente /hold/1, /hold/2 y /ok en el proxy y en el origen. Sus comprobaciones de limpieza no informaron de manejadores ni escritores de fixture pendientes. Esta es una demostración ejecutada de comportamiento local; no mide la latencia, la disponibilidad ni el rendimiento de un proxy externo.
La respuesta recuperada importa porque muestra que el cliente puede avanzar sin cambiar el proxy, el destino ni el límite de conexiones. La ausencia de /blocked en el proxy ofrece una segunda observación de dónde se detuvo este intento concreto. No generalices esa traza hasta afirmar que todo timeout del pool es una fuga: los streams largos legítimos y más trabajo concurrente que conexiones disponibles también pueden crear una espera.
Arregla la propiedad antes de añadir capacidad
Para el trabajo de streaming normal, mantén el ciclo de vida de la respuesta dentro de un gestor de contexto. El comprobador descargado posee tanto la respuesta como su bucle de lectura acotado; su resultado se devuelve solo después de que el stream se haya consumido o la operación haya fallado. Para el streaming manual, cierra la respuesta con await response.aclose() en cada ruta de salida, incluidas las excepciones y la cancelación. Documentación de streaming de HTTPX.
Cerrar una respuesta libera sus recursos; no promete que una conexión HTTP/1.1 consumida parcialmente sea reutilizable. La siguiente petición puede necesitar una conexión nueva. Si cada stream abierto sigue teniendo trabajo útil, puede ser apropiado reducir la concurrencia admitida o elegir un pool acotado más grande. Si las respuestas abandonadas siguen poseyendo conexiones, un pool más grande solo pospone el mismo problema. Cierre de respuesta etiquetado, propiedad del pool de conexiones en HTTPcore.
Distingue también una conexión silenciosa de una operación completa lenta. Un timeout de lectura de HTTPX limita la espera de un fragmento de datos; no es un plazo para descargar una respuesta entera que llega lentamente. El comprobador añade un plazo separado para toda la operación. Una cola de producción también necesita su propia política de admisión, memoria, cancelación y apagado; esta demostración de tres peticiones no es un sistema de workers completo. Timeouts de HTTPX, plazos de operación en Python.
Comprueba tu propia ruta con el ejemplo completo
Haz que tu entorno o gestor de secretos proporcione PROXY_URL y una TARGET_URL autorizada, y luego ejecuta:
python async_proxy_check.pyEsto envía tres GET. Elige un endpoint pequeño que controles o que tengas permiso para probar; evita una URL cuya operación GET desencadene trabajo que no pretendes repetir. El comprobador imprime el estado, el número de bytes decodificados y un resumen SHA-256 en caso de éxito, o una clase de error en caso de fallo. No imprime las URL, los cuerpos de respuesta ni los mensajes de excepción en bruto, que pueden contener datos sensibles.
Un resumen coincidente solo muestra que los cuerpos observados coinciden. No demuestra que contengan contenido útil: una página de desafío repetida también puede tener un resumen estable. La demostración local comprueba por separado su cuerpo JSON esperado. Añade la comprobación de contenido equivalente para tu destino antes de considerar exitosa tu propia ruta.
Usa el resultado para elegir la siguiente investigación:
PoolTimeout: inspecciona primero la propiedad de las respuestas abiertas, el trabajo concurrente y los límites de espera del pool. Usa una traza como la del ejemplo local para determinar si un intento llegó al proxy.- Errores de conexión o de proxy: investiga el gateway configurado y la etapa que falló. Una excepción del proxy y un estado HTTP del destino son observaciones distintas.
ReadTimeoutuOperationDeadline: averigua qué seguía esperando cuando expiró el límite. El plazo de toda la operación también incluye el tiempo de espera de admisión.HTTPStatusErroroBodyTooLarge: inspecciona la política de respuesta y el recurso esperado. Aumentar el pool de conexiones no cambia ninguna de las dos comprobaciones.
La guía de timeouts de red cubre el diagnóstico de las etapas de DNS, TCP, CONNECT, TLS y cuerpo. La guía de variables de entorno compara el comportamiento de enrutamiento en otros clientes. Para código Python síncrono, usa la guía de proxy para Requests aparte.
Descargas y método
El archivo incluye el comprobador completo, la demostración en loopback, las dependencias fijadas, la suite de pruebas y el README. Los archivos individuales también están disponibles: comprobador, demostración, requirements, pruebas, README y salida registrada.
Método: ipvolt ejecutó comprobaciones controladas en loopback el 13 de septiembre de 2026 usando las versiones fijadas indicadas arriba. Los casos ejercitados cubren el agotamiento y la recuperación del pool, el comprobador completo, los fallos por estado HTTP y por cuerpo demasiado grande, y la limpieza tras cancelación. El fixture envía Connection: close, así que demuestra la reutilización de un cliente y la liberación de capacidad del pool, no la reutilización TCP con keep-alive. Estas comprobaciones no establecen el comportamiento para todas las versiones de Python, sistemas operativos, servidores HTTP/2, esquemas de autenticación ni productos de proxy.
Si quieres enterarte cuando se abra el acceso a ipvolt, únete a la lista de acceso anticipado. Un correo cuando se abra el acceso. Nada más. Estos ejemplos son diagnósticos de cliente, no documentación de un endpoint de ipvolt disponible.
Fuentes y lecturas adicionales
Referencias técnicas usadas para esta guía. Consulta la documentación de tu versión instalada y la configuración compatible de tu proveedor.