versiones taglibs standard pagina last jakarta impl apache tomcat ajp

taglibs - tomcat apache server



Error de Apache/Tomcat: páginas erróneas entregadas (11)

Este error me ha estado volviendo loco. Tenemos un servidor que ejecuta Apache y Tomcat, que sirve múltiples sitios diferentes. Normalmente, el servidor funciona bien, pero a veces ocurre un error en el que a las personas se les envía la página incorrecta, ¡ la página que alguien más solicitó!

Pistas:

  • Las páginas que se entregan son las que otro usuario solicitó recientemente y, de lo contrario, se entregan correctamente. Se sabe que se intercambian dos solicitudes simultáneas. Por lo que puedo decir, ninguna de las páginas que se entregan incorrectamente tiene más de unos pocos minutos.
  • Solo afecta a los archivos que está sirviendo Tomcat. Los archivos estáticos como las imágenes no se ven afectados.
  • No sucede todo el tiempo. Cuando sucede, sucede para todos.
  • Parece suceder en momentos de demanda máxima. Sin embargo, la demanda aún no es muy alta, sin duda está dentro de los límites de lo que Apache puede enfrentar.
  • Reiniciar Tomcat lo arregló, pero solo por unos minutos. Al reiniciar, Apache lo arregló, pero solo por unos minutos.
  • El servidor ejecuta Apache 2 y Tomcat 6, usando una VM Java 6 en Gentoo. La conexión es con AJP13, y JkMount directivas JkMount dentro de los bloques <VirtualHost> son correctas.
  • No hay nada de uso en ninguno de los archivos de registro.

Más información:

Apache no tiene ningún tipo de almacenamiento en caché activado. Todas las entradas relacionadas con el almacenamiento en caché en httpd.conf e importaciones relacionadas dicen, por ejemplo:

<IfDefine CACHE> LoadModule cache_module modules/mod_cache.so </IfDefine>

Si bien las opciones para Apache no incluyen esa bandera:

APACHE2_OPTS="-D DEFAULT_VHOST -D INFO -D LANGUAGE -D SSL -D SSL_DEFAULT_VHOST -D PHP5 -D JK"

Tomcat tampoco tiene opciones de almacenamiento en caché activadas, que yo pueda encontrar.

La sugerencia del toolkit fue buena, pero no apropiada en este caso. Lo que me lleva a creer que el error no puede estar dentro de mi propio código es que no se trata simplemente de unos pocos valores que se transfieren: es la solicitud completa, incluida la URL, los parámetros, las cookies de sesión, todo. La gente recibe páginas que dicen "has iniciado sesión como John", cuando claramente no lo son.

Actualizar:

En base a las sugerencias de varias personas, voy a agregar los siguientes encabezados HTTP a las páginas servidas por Tomcat para deshabilitar todas las formas de almacenamiento en caché:

Cache-Control: no-store Vary: *

Afortunadamente, estos encabezados serán respetados no solo por Apache, sino también por otros cachés o proxies que puedan estar en el camino. Desafortunadamente no tengo forma de reproducir este error deliberadamente, así que tendré que esperar y ver si aparece nuevamente.

Noté que se están incluyendo los siguientes encabezados, ¿podrían relacionarse de alguna manera?

Connection: Keep-Alive Keep-Alive: timeout=5, max=66

Actualizar:

Al parecer, esto volvió a ocurrir mientras yo estaba dormido, pero ha dejado de suceder ahora que estoy despierto para verlo. Una vez más, no hay nada útil en los registros que pueda ver, así que no tengo pistas sobre lo que realmente estaba sucediendo o cómo prevenirlo.

¿Hay alguna información adicional que pueda poner en los registros de Apache o Tomcat para que sea más fácil de diagnosticar?

Actualizar:

Como esto ha sucedido nuevamente un par de veces, hemos cambiado la forma en que Apache se conecta con Tomcat para ver si afecta las cosas. Estábamos usando mod_jk con una directiva como esta:

JkMount /portal ajp13

Cambiamos ahora a usar mod_proxy_ajp , así:

ProxyPass /portal ajp://localhost:8009/portal

Veremos si hace alguna diferencia. Este error siempre fue molestamente impredecible, por lo que nunca podemos decir definitivamente si funcionó o no.

Actualizar:

Acabamos de recibir el error brevemente en un sitio que se dejó usando mod_jk , mientras que un sitio hermano en el mismo servidor que usa mod_proxy_ajp no mostró el error. Esto no prueba nada, pero proporciona evidencia de que swithing a mod_proxy_ajp puede haber ayudado.

Actualizar:

Acabamos de recibir el error nuevamente anoche en un sitio usando mod_proxy_ajp , por lo que claramente no lo ha resuelto - mod_jk no fue la fuente del problema. Voy a intentar la sugerencia anónima de desactivar las conexiones persistentes:

KeepAlive Off

Si eso también falla, voy a estar lo suficientemente desesperado como para comenzar a investigar GlassFish.

Actualizar:

¡Maldición! El problema acaba de regresar. No lo había visto en mucho tiempo, así que comencé a pensar que finalmente lo habíamos resuelto. Odio heisenbugs.


¿Estás seguro de que esa es la página que alguien más solicitó o una página sin parámetros ?, podrías obtener errores extraños si tu conexiónTimeout es demasiado corta en server.xml en el servidor tomcat detrás de apache, increméntala a un número mayor:

configuración predeterminada:

<Connector port="8080" protocol="HTTP/1.1" connectionTimeout="20000" redirectPort="8443" />

cambiado:

<Connector port="8080" protocol="HTTP/1.1" connectionTimeout="2000000" redirectPort="8443" />


Compruebe si sus encabezados permiten el almacenamiento en caché sin el encabezado HTTP Vary correcto (si usa cookies de sesión, por ejemplo, y permite el almacenamiento en caché, necesita una entrada en el encabezado Vary HTTP para el encabezado de la cookie, o un caché / proxy puede servir la versión en caché de una página destinada a un usuario a otro usuario).

El problema podría ser no almacenar en caché en su servidor web, sino en otra capa de almacenamiento en caché (ya sea en un proxy inverso en frente de su servidor web, o en un proxy cerca de los usuarios). Si los clientes están transfiriendo un NAT, también podrían estar detrás de un proxy transparente (y, para hacer las cosas aún más difíciles de depurar, el proxy transparente podría estar configurado para no estar visible en los encabezados).



Tuve este problema y realmente me volvió loco. No sé por qué, pero lo resolví apagando el Keep Alive en el http.conf

de

KeepAlive en

a

KeepAlive desactivado

Mi aplicación no usa la característica keepalive, por lo que funcionó muy bien para mí.


8 actualizaciones de la pregunta más adelante un problema más para usar para probar / reproducir, aunque puede ser difícil (o costoso) para sitios públicos.

Puede habilitar https en los sitios. Esto al menos eliminaría cualquier otro caché de proxies en el camino. Sería malo ver que hay algunos bloqueadores de carga olvidados o cachés de empresas en el camino que interfieren con su tráfico.

Para los sitios públicos esto implicaría certificados de confianza en las claves, por lo que se involucrará algo de dinero. Para probar las claves con firma automática puede ser suficiente. Además, verifique que no haya ningún proxy transparente involucrado que descifre y vuelva a generar el tráfico. (son fácilmente detectables, ya que no pueden usar el mismo certificado / clave que el servidor original)


Prueba esto:

response.setHeader("Cache-Control", "no-cache"); //HTTP 1.1 response.setHeader("Pragma", "no-cache"); //HTTP 1.0 response.setDateHeader("Expires", 0); //prevents caching at the proxy server


Eche un vistazo a este sitio, describe un problema con mod_jk. Me encontré con su publicación mientras miraba un problema muy similar. Básicamente, la solución es actualizar a una versión más reciente de mod_jk. Todavía no he tenido la oportunidad de implementar el cambio en nuestro servidor, pero intentaré esto mañana y veré si ayuda.

http://securitytracker.com/alerts/2009/Apr/1022001.html


Cambiamos Apache de proxying con AJP a proxying con HTTP. Hasta ahora, parece que ha resuelto el problema, o al menos lo ha reducido enormemente: el problema no se ha informado en meses, y el uso de la aplicación ha aumentado desde entonces.

El cambio está en el httpd.conf de Apache. Habiendo comenzado con mod_jk :

JkMount /portal ajp13

Cambiamos a mod_proxy_ajp :

ProxyPass /portal ajp://localhost:8009/portal

Luego, finalmente, a la recta mod_proxy :

ProxyPass /portal http://localhost:8080/portal

Tendrá que asegurarse de que Tomcat esté configurado para servir HTTP en el puerto 8080. Y recuerde que si está atendiendo / , debe incluir / en ambos lados del proxy o comienza a llorar:

ProxyPass / http://localhost:8080/


Puede que no sea un problema de almacenamiento en caché. Intenta aumentar el parámetro MaxClients en apache2.conf. Si es demasiado bajo (¿150 de manera predeterminada?), Apache comienza a hacer solicitudes en cola. Cuando decide servir la solicitud en cola a través de mod_proxy, saca una página incorrecta (o puede que solo esté estresada haciendo todas las colas).


Aunque mencionó que mod_cache no se habilitó en su configuración, para otros que pueden haber encontrado el mismo problema con mod_cache habilitado (incluso en contenidos estáticos), la solución es asegurarse de que la siguiente directiva esté habilitada en el encabezado Set-Cookie HTTP:

CacheIgnoreHeaders Set-Cookie

El motivo es que mod_cache guardará en caché el encabezado Set-Cookie que puede ser servido a otros usuarios. Esto luego se filtraría el ID de sesión del usuario que llenó por última vez el caché a otro.


¿Podría ser la seguridad de subprocesos de tus servlets?

Sus servidores almacenan cualquier información en los miembros de la instancia.

Por ejemplo, algo tan simple como lo siguiente puede causar problemas relacionados con hilos:

public class MyServlet ... { private String action; public void doGet(...) { action = request.getParameter("action"); processAction(response); } public void processAction(...) { if (action.equals("foo")) { // send foo page } else if (action.equals("bar")) { // send bar page } } }

Debido a que se accede al serlvet por múltiples hilos, no hay garantía de que el miembro de la instancia de acción no sea derrotado por otra persona y termine enviando la página equivocada.

La solución simple a este problema es usar variables locales insead de miembros de instancia:

public class MyServlet ... { public void doGet(...) { String action = request.getParameter("action"); processAction(action, response); } public void processAction(...) { if (action.equals("foo")) { // send foo page } else if (action.equals("bar")) { // send bar page } } }

Nota: ¿esto se extiende también a las páginas de JavaServer, si las enviaba para sus vistas?