104, error de ‘reinicio de la conexión por el par’, o ¿Cuándo cerrar un socket da como resultado un RST en lugar de FIN?

Estamos desarrollando un servicio web de Python y un sitio web de cliente en paralelo. Cuando realizamos una solicitud HTTP del cliente al servicio, una llamada genera un socket.error en socket.py de manera consistente, en lectura:

  (104, 'Conexión restablecida por par') 

Cuando escucho con wireshark, las respuestas “buenas” y “malas” son muy similares:

  • Debido al tamaño del encabezado OAuth, la solicitud se divide en dos paquetes. El servicio responde a ambos con ACK.
  • El servicio envía la respuesta, un paquete por encabezado (HTTP / 1.0 200 OK, luego el encabezado de Fecha, etc.). El cliente responde a cada uno con ACK.
  • (Buena solicitud) el servidor envía un FIN, ACK. El cliente responde con un FIN, ACK. El servidor responde ACK.
  • (Solicitud incorrecta) el servidor envía un RST, ACK, el cliente no envía una respuesta TCP, el socket.error se genera en el lado del cliente.

Tanto el servicio web como el cliente se ejecutan en una caja Gentoo Linux x86-64 que ejecuta glibc-2.6.1. Estamos usando Python 2.5.2 dentro de la misma virtual_env.

El cliente es una aplicación Django 1.0.2 que está llamando a httplib2 0.4.0 para realizar solicitudes. Estamos firmando solicitudes con el algoritmo de firma OAuth, con el token OAuth siempre configurado en una cadena vacía.

El servicio ejecuta Werkzeug 0.3.1, que utiliza el wsgiref.simple_server de Python. Ejecuté la aplicación WSGI a través de wsgiref.validator sin problemas.

Parece que esto debería ser fácil de depurar, pero cuando busco una buena solicitud en el lado del servicio, se parece a la solicitud incorrecta, en la función socket._socketobject.close (), convirtiendo los métodos de delegado en métodos ficticios. Cuando se desactiva el método enviar o enviar (no puedo recordar cuál), se envía FIN o RST, y el cliente comienza a procesar.

“El restablecimiento de la conexión por parte de un par” parece culpar al servicio, pero tampoco confío en httplib2. ¿Puede el cliente ser culpable?

** Depuración adicional: parece un servidor en Linux **

Tengo un MacBook, así que intenté ejecutar el servicio en uno y el sitio web del cliente en el otro. El cliente de Linux llama al servidor OS X sin el error (FIN ACK). El cliente OS X llama al servicio de Linux con el error (RST ACK, y un (54, ‘Conexión restablecida por el par’)). Por lo tanto, parece que es el servicio que se ejecuta en Linux. ¿Es x86_64? Un mal glibc? wsgiref? Sigue buscando…

** Pruebas adicionales – wsgiref se ve escamosa **

Hemos iniciado la producción con Apache y mod_wsgi, y los restablecimientos de conexión han desaparecido. Vea mi respuesta a continuación, pero mi consejo es que registre el restablecimiento de la conexión y vuelva a intentarlo. Esto permitirá que su servidor funcione correctamente en modo de desarrollo, y sólidamente en producción.

He tenido este problema. Consulte el problema “Restablecimiento de la conexión por pares” de Python .

Es muy probable que se encuentre en conflicto con pequeños problemas de tiempo basados ​​en el locking global de intérprete de Python.

Puede (a veces) corregir esto con un time.sleep(0.01) colocado estratégicamente.

“¿Dónde?” usted pregunta. Me golpea La idea es proporcionar una mejor concurrencia de hilos en y alrededor de las solicitudes del cliente. Intente colocarlo justo antes de realizar la solicitud para que la GIL se reinicie y el intérprete de Python pueda borrar cualquier subproceso pendiente.

No use wsgiref para la producción. Usa Apache y mod_wsgi, o algo más.

Continuamos viendo estos restablecimientos de conexión, a veces con frecuencia, con wsgiref (el backend utilizado por el servidor de prueba werkzeug, y posiblemente otros como el servidor de prueba Django). Nuestra solución fue registrar el error, reintentar la llamada en un bucle y abandonar después de diez fallos. httplib2 lo intenta dos veces, pero necesitamos algunos más. Parece que también vienen en grupos: agregar 1 segundo de sueño podría solucionar el problema.

Nunca hemos visto un restablecimiento de la conexión cuando se ejecuta a través de Apache y mod_wsgi. No sé qué es lo que hacen de manera diferente (tal vez solo los enmascaran), pero no aparecen.

Cuando le pedimos ayuda a la comunidad de desarrolladores locales, alguien confirmó que ve una gran cantidad de restablecimientos de conexión con wsgiref que desaparecen en el servidor de producción. Hay un error allí, pero será difícil encontrarlo.

Me doy cuenta de que estás usando python, pero encontré que este artículo de Java es útil.

http://java.sun.com/javase/6/docs/technotes/guides/net/articles/connection_release.html

Normalmente, obtendría un RST si realiza un cierre que no perdura (es decir, en el que la stack puede descartar los datos si no se han enviado y ACK’d) y un FIN normal si permite el cierre. para permanecer (es decir, el cierre espera a que los datos en tránsito sean ACK’d).

¿Tal vez todo lo que necesita hacer es configurar su zócalo para que permanezca de modo que elimine la condición de carrera entre un cierre no prolongado realizado en el zócalo y los ACK que llegan?

Sin embargo, tuve el mismo problema al hacer una carga de un archivo muy grande con un cliente de solicitudes python que publica en un backend de nginx + uwsgi.

Lo que terminó siendo la causa fue que el backend tenía un límite en el tamaño máximo de archivo para cargas inferiores a las que el cliente intentaba enviar.

El error nunca apareció en nuestros registros uwsgi, ya que este límite fue en realidad uno impuesto por nginx.

El aumento del límite en nginx eliminó el error.