Requests: Demasiados archivos abiertos

Creado en 5 nov. 2011  ·  81Comentarios  ·  Fuente: psf/requests

Estoy construyendo un generador de carga básico y comencé a correr dentro de los límites del descriptor de archivos, no he visto ninguna documentación relacionada con cómo liberar recursos, así que lo estoy haciendo mal y los documentos deben actualizarse, o las solicitudes están filtrando descriptores de archivos en alguna parte (sin soporte para keepalive, estoy un poco confundido sobre por qué los archivos se dejarían abiertos)

Bug Contributor Friendly

Comentario más útil

"Demasiados archivos abiertos" es el resultado del error causado por los sockets que permanecen en CLOSE_WAIT.
Por lo tanto, ulimit no solucionará, solo haga una solución.

Todos 81 comentarios

¿Dónde usas requests.async ?

no, todas las solicitudes eran request.get / request.post razonablemente sencillas, todavía veo algunas allí

$ lsof | grep localhost | wc -l
110

todos menos 4/5 son del formato

Python    82117 daleharvey  123u    IPv4 0xffffff800da304e0       0t0      TCP localhost:61488->localhost:http (CLOSE_WAIT)

Estoy un poco desconcertado por esto, para ser honesto.

Hah, intentaré reproducirlo de forma fiable, si no puedo cerrar

He visto que me sucede esto, pero solo cuando estoy usando el módulo asíncrono con más de 200 conexiones simultáneas.

Hola,
Obtuve exactamente el mismo problema al usar solicitudes y parches de mono con gevent: algunas conexiones permanecen en CLOSE_WAIT.
Quizás un problema con Gevent.

Puede ser un problema de ulimit -n. Pruebe con un valor más alto.

"Demasiados archivos abiertos" es el resultado del error causado por los sockets que permanecen en CLOSE_WAIT.
Por lo tanto, ulimit no solucionará, solo haga una solución.

@tamiel ¿cómo solucionamos esto?

Haré más pruebas lo antes posible e intentaré arreglarlo.

Lo he investigado y parece que hay un problema con todas las bibliotecas que utilizan httplib.HTTPSConnection.

Publicado un ejemplo aquí:

https://gist.github.com/1512329

Acabo de encontrar un error muy similar al usar un grupo asíncrono con solo conexiones HTTP; todavía estoy investigando, pero pasar un tamaño de grupo a async.map hace que el error se reproduzca rápidamente.

¿Alguna solución a esto? Esto hace que las solicitudes sean inutilizables con gevent ..

Se trata de los CLOSE_WAIT s. Solo tengo que cerrarlos. Sin embargo, no estoy seguro de por qué todavía están abiertos.

¿Es un problema de urllib3? Tener que cerrar estos por nosotros mismos no es una gran idea, creo.

Es más un problema general. Podemos mantener la conversación aquí.

Ok, solo para darle una perspectiva, estamos tratando de pasar de httplib2 a las solicitudes, y no vemos este problema con httplib2. Por tanto, no es un problema generalizado.

Por general quiero decir que es un problema muy serio que afecta a todos los involucrados.

Entonces, ¿cómo resolvemos esto? realmente queremos usar las solicitudes + el sueño en el futuro

Me encantaría saber la respuesta a eso.

La filtración parece deberse al manejo de redireccionamiento interno, que hace que se generen nuevas solicitudes antes de que se consuman las respuestas pendientes. En la prueba, acdha @ 730c0e2e2bef77968a86962f9d5f2bebba4d19ec tiene una solución poco satisfactoria pero efectiva simplemente al obligar a consumir cada respuesta antes de continuar.

Esto requirió cambios en dos lugares, lo que me hace querer refactorizar ligeramente la interfaz, pero no tengo tiempo para continuar.

399 tiene una solución que funciona bien en mi generador de carga asíncrono (https://github.com/acdha/webtoolbox/blob/master/bin/http_bench.py) con miles de solicitudes y un bajo fd ulimit

Me he encontrado con el mismo problema al usar async: obtuve una solución al dividir las solicitudes y eliminar las respuestas / llamar a gc.collect

Creo que me encontré con esto hoy al conectarme a un servidor con licencia que solo permite 5 conexiones.

Usando async, solo pude OBTENER 4 cosas antes de que se detuviera durante 60 segundos.

Usando el GET normal con el consumo, pude obtener alrededor de 150 cosas en serie en menos de 40 segundos.

Todavía no he hecho mi kludge desde que vi este problema.

Recibí este error mientras usaba ipython y recibí este mensaje. Esto es solo hacer cada solicitud una a la vez, pero creo que obtuve algo similar al usar async.

ERROR: Internal Python error in the inspect module.
Below is the traceback from this internal error.
Traceback (most recent call last):
    File "/Library/Python/2.7/site-packages/IPython/core/ultratb.py", line 756, in structured_traceback
    File "/Library/Python/2.7/site-packages/IPython/core/ultratb.py", line 242, in _fixed_getinnerframes
    File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/inspect.py", line 1035, in getinnerframes
    File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/inspect.py", line 995, in getframeinfo
    File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/inspect.py", line 456, in getsourcefile
    File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/inspect.py", line 485, in getmodule
    File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/inspect.py", line 469, in getabsfile
    File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/posixpath.py", line 347, in abspath
OSError: [Errno 24] Too many open files

Unfortunately, your original traceback can not be constructed.

Curiosamente, creo que cuando utilizo solo el intérprete normal de Python obtengo un "Error máximo de reintentos", pero creo que es otro problema para mí al hacer solicitudes en el mismo dominio, pero no estoy seguro.

Me encontré con esto en el primer proyecto que tuve donde allow_redirects era True; parece deberse a que la cadena de redireccionamiento filtra objetos de respuesta que no se liberan ni siquiera con prefetch = True. Esto lo solucionó en mi prueba inicial:

        [i.raw.release_conn() for i in resp.history]
        resp.raw.release_conn()

Hmmm ...

Configuración de @acdha :

requests.defaults.defaults['allow_redirects'] = False

antes de hacer cualquier solicitud, todavía da como resultado el mismo error, pero creo que esta no es una opción para mi implementación, ya que todas las solicitudes que estoy haciendo requerirán una redirección = /

@dalanmiller ¿Cómo procesa sus respuestas? Anteriormente estaba usando async.map con un gancho de respuesta y _ parece_ ser más estable usando un bucle simple sobre async.imap :

for resp in requests.async.imap(reqs, size=8):
    try:
        print resp.status_code, resp.url
    finally:
        [i.raw.release_conn() for i in resp.history]
        resp.raw.release_conn()

@acdha

Solo estaba usando un bucle for a través de una lista de URL y haciendo una solicitud.Obtenga cada uno con mi configuración y demás.

for u in urls:
    response_list.append(requests.get(u))

Intenté usar su pasta y funciona para aproximadamente 50 solicitudes en mi lista de 900 de longitud, hasta que empiezo a obtener "errores máximos de reintentos excedidos con URL" para el resto. Sin embargo, este es un error bastante estándar para acceder al mismo dominio repetidamente, ¿no?

Oye, estaba rastreando una enorme lista de URL, 35k, y obtuve el mismo error en _algunas_ solicitudes.

Recibo URL en trozos de 10, como este:

responses = requests.async.map([requests.async.get(u, params=self.params()) for u in chunk]) # chunk is a list of 10

En algún lugar en el rango de 20k comencé a recibir el error 24, luego estuvo bien hasta 30k y luego nuevamente.

¿Alguna información adicional que le interesaría para reducirla?

requests.async se ha ido. Es posible que desee considerar pasar a grequests.

Muy bien, gracias. Sería bueno mencionar esto en los documentos.

Una especie de novato cuando se trata de solicitudes de extracción y redacción de documentación, pero lo probé y lo envié. Por favor comente o critique :)

https://github.com/kennethreitz/requests/pull/665

Ok, esto sucede incluso sin usar async, con solo request.get, después de 6K solicitudes.

Lo sospechaba.

Para mí, el error 'Demasiados archivos abiertos' se produjo después de descargar exactamente 1k archivos. Mi solución fue deshabilitar la propiedad de mantener vivo, siempre recibiendo solicitudes en fragmentos ( @acdha, gracias por la pista). lsof -p PID | wc -l muestra un número no creciente de conexiones durante la ejecución.

rsess = requests.session()
rsess.config['keep-alive'] = False

rs = [grequests.get(l, session=rsess) for l in links]

for s in chunks(rs,100):
    responses = grequests.map(s, size=concurrency)
    for r in responses:
        try:
            print(r.status_code, r.url)
        finally:
            r.raw.release_conn()

[1] fragmentación: http://stackoverflow.com/a/312464

Cerrando mientras se aplaza la corrección de urllib3.

@kennethreitz ¿Cuál es el número de problema de urllib3?

Parece que este es el problema http://bugs.python.org/issue16298

@silvexis podría muy bien estar relacionado con el error urllib3, ahora solo deseo que alguien haya respondido @ piotr-dobrogost: P

¿Alguien más todavía tiene este problema?

No he escuchado ningún informe al respecto. ¿Es usted?

Es un problema de la configuración de la caja, no del marco. Mire la configuración del kernel de su sistema operativo. En BSD se llama kern.maxfiles . Hay hilo sobre ulimit en sistemas Linux: http://stackoverflow.com/questions/34588/how-do-i-change-the-number-of-open-files-limit-in-linux
Espero que ayude, y no sé cómo cambiar este parámetro en Windows.

Con la salvedad de que todavía estamos ejecutando una versión anterior de las solicitudes, tenemos el siguiente código horrible para manejar esto:

    if self._current_response is not None:
            # Requests doesn't have a clean API to actually close the
            # socket properly. Dig through multiple levels of private APIs
            # to close the socket ourselves. Icky.
            self._current_response.raw.release_conn()
            if self._current_response.raw._fp.fp is not None:
                sock = self._current_response.raw._fp.fp._sock
                try:
                    logger.debug('Forcibly closing socket')
                    sock.shutdown(socket.SHUT_RDWR)
                    sock.close()
                except socket.error:
                    pass

(Creo que self._current_response es el objeto de respuesta de las solicitudes)

Hmm, ¿dónde está rota la cadena de cierre? Tenemos un método Response.close() que llama a release_conn() , entonces, ¿qué debe suceder en release_conn() para que esto funcione?

@Lukasa esto definitivamente se solucionó en urllib3 ya que yo era parte de la discusión. Con una inclinación a ser conservador en mi estimación, diría que está ahí ya que solicita 1.2.x si no 1.1.x.

Sí, pensé que esto estaba arreglado. A menos que veamos algo en 1.2.3, continuaré asumiendo que esto está arreglado.

Veo una fuga CLOSE_WAIT con 2.0.2, ¿tiene pruebas unitarias para asegurarse de que no haya regresión sobre el tema?

No, no lo hacemos. AFAIK urllib3 tampoco. ¿Puedes reproducir tu fuga fácilmente?

Usamos request en nuestra aplicación interna desde el lunes, y hoy alcanzamos los 1024 maxfiles.

2 horas después del reinicio, tenemos 40 CLOSE_WAIT según lo indicado por lsof.

Así que creo que seremos capaces de reproducir en un entorno de desarrollo, sí. Voy a mantenerte en contacto

@tardyp también, ¿cómo instalaste las solicitudes? Creo que todos los mantenedores de paquetes del sistema operativo eliminan urllib3. Si no lo mantienen actualizado y estás usando una versión anterior, esa podría ser la causa. Si está utilizando pip, no dude en abrir un nuevo número para realizar un seguimiento en lugar de agregar una discusión sobre este.

Lo instalé con pip, pero uso python 2.6, he visto una solución en python2.7 para
este error. ¿Tiene parche de mono para la versión anterior?

Pierre

El viernes 29 de noviembre de 2013 a las 5:33 p.m., Ian Cordasco [email protected] escribió:

@tardyp https://github.com/tardyp también, ¿cómo instalaste las solicitudes? yo
creo que todos los mantenedores de paquetes del sistema operativo eliminan urllib3. Si no lo hacen
mantén eso actualizado y estás usando una versión anterior, que podría ser la
causa en su lugar. Si está utilizando pip, no dude en abrir una nueva edición para
rastrear esto con en lugar de agregar discusión sobre este.

-
Responda a este correo electrónico directamente o véalo en Gi
.

@tardyp , abra un nuevo problema con la mayor cantidad de detalles posible, incluido si las solicitudes que realiza tienen redireccionamientos y si está utilizando gevent. Además, cualquier detalle sobre el sistema operativo y un ejemplo de cómo reproducirlo sería fantástico.

FYI https://github.com/shazow/urllib3/issues/291 se ha revertido debido a errores.

¿Deberíamos volver a abrir esto?
¡Tengo el mismo problema!

@polvoazul No hay forma de que este sea el mismo problema, que se informó originalmente en 2011, por lo que no creo que la reapertura sea correcta. Sin embargo, si está ejecutando la versión actual de solicitudes (2.4.3) y puede reproducir el problema, abrir un nuevo problema sería correcto.

@Lukasa necesito tu ayuda。 uso eventlet + solicitudes , que siempre crean tantos calcetines que no pueden identificar el protocolo。 mis solicitudes son 2.4.3, ¿eventlet + solicitudes causan este problema?

Lo siento @mygoda , pero es imposible saberlo. Si no está limitando la cantidad de solicitudes que pueden estar pendientes en un momento dado, entonces ciertamente es posible, pero ese es un problema arquitectónico fuera del alcance de las solicitudes.

@Lukasa gracias。 creo que mi problema es similar con esto 。 mi proyecto es pyvmomi . esa conexión es de larga duración. Siempre confundí por qué puedo contener tantos no puedo identificar el calcetín de protocolo

Tener el mismo problema ahora, ejecutar 120 subprocesos, causa más de 100000 archivos abiertos, ¿alguna solución en este momento?

@mygoda usas períodos increíbles。

@ 1a1a11a _¿Qué_ archivos tienes abiertos? Ese sería un primer paso útil para comprender este problema.

@ 1a1a11a ¿Qué versión de las solicitudes estás usando? ¿Qué versión de Python? ¿Qué sistema operativo? ¿Podemos obtener alguna información?

Estoy usando la solicitud 2.9.1, python 3.4, ubuntu 14.04, básicamente estoy escribiendo un rastreador usando 30 subprocesos con proxies para rastrear algún sitio web. Actualmente he ajustado el límite de archivos por proceso a 655350; de lo contrario, informará de un error.

Sigo recibiendo el error "No se pudo establecer una nueva conexión: [Errno 24] Demasiados archivos abiertos" de request.packages.urllib3.connection.VerifiedHTTPSConnection. "Estoy usando Python 3.4, solicitudes 2.11.1 y solicitudes-futuros 0.9.7. Aprecio que las solicitudes-futuros es una biblioteca separada, pero parece que el error proviene de las solicitudes. Estoy intentando realizar 180.000 solicitudes asíncronas a través de SSL. He dividido esas solicitudes en segmentos de 1000, así que solo pasar a los siguientes 1000 una vez que se hayan resuelto todos los objetos futuros. Estoy ejecutando Ubuntu 16.04.2 y mi límite de archivos abiertos predeterminado es 1024. Sería bueno comprender la razón subyacente de este error. ¿un archivo abierto para cada solicitud individual? Y si es así, ¿por qué? ¿Es este un archivo de certificado SSL? ¿Y la biblioteca de solicitudes cierra automáticamente esos archivos abiertos cuando se resuelve el objeto futuro?

Las solicitudes abren muchos archivos. Algunos de esos archivos se abren para certificados, pero OpenSSL y no Solicitudes los abre, por lo que no se mantienen. Además, las solicitudes también abrirán, si es necesario, el archivo .netrc , el archivo hosts y muchos otros.

Lo mejor será utilizar una herramienta como strace para averiguar qué archivos se abren. Existe una lista estricta de llamadas al sistema que conducen a la asignación de descriptores de archivos, por lo que debería poder enumerarlos con razonable rapidez. Eso también le permitirá saber si hay un problema o no. Pero, sí, esperaría que si está realizando activamente 1000 conexiones a través de HTTPS, con la carga máxima podríamos usar fácilmente más de 1000 FD.

También luché con este problema y descubrí que usar opensnoop en OS X funcionó muy bien para permitirme ver qué estaba sucediendo si alguien se encuentra con los mismos problemas.

También veo con frecuencia este error cuando llamo repetidamente a requests.post(url, data=data) a un servidor HTTP (no HTTPS). Ejecutando en Ubuntu 16.04.3, Python 3.5.2, solicitudes 2.9.1

¿Qué es data ?

Unos cientos de kb de texto

¿No es un objeto de archivo?

No, formo una gran consulta en la memoria.

¿Está ejecutando este código en varios subprocesos?

No, solo hilo, publicando en localhost

Parece casi imposible que filtremos tantos FD entonces: deberíamos usar repetidamente la misma conexión TCP o cerrarla agresivamente. ¿Quiere comprobar lo que hace su servidor?

Estoy teniendo este problema Python 2.7, solicitudes 2.18.4, urllib3 1.22.
Ejecutando código multiproceso (no multiprocesado). Conectarse a un máximo de 6 URL a la vez, creando y cerrando manualmente una nueva sesión para cada una.

Tengo el mismo problema en Python 3.5 , requests==2.18.4

@mcobzarenco, ¿está seguro de que está (implícitamente) cerrando la conexión subyacente de la respuesta? Simplemente devolver la respuesta no cerrará la conexión. Al leer response.content, los datos se leen realmente y después de eso, el socket no permanecerá en CLOSE_WAIT.

¿Fue útil esta página
0 / 5 - 0 calificaciones