viernes, 22 de febrero de 2013

Sesiones, Tomcat y consumo de memoria

Dado que en nuestro caso, además de desarrollar las aplicaciones, gestionamos los servidores de aplicaciones (la parte software), de vez en cuando hago revisiones periódicas del consumo de memoria, versiones de librerías… y en la última revisión encontré algunas cosas interesantes, pero una me llamó bastante la atención:
El consumidor #1 de memoria de los servidores de aplicaciones era… ¡El gestor de sesiones del Tomcat! Aunque parezca una perogrullada, el motivo de mi sorpresa es que nosotros no guardamos apenas datos en sesión, y en muchos casos ni siquiera usamos la sesión para nada puesto que muchas de nuestras aplicaciones son simplemente de consulta.
Así que revisando, revisando, encontré que el problema venía dado por varios factores:

  • Aunque nosotros no usáramos la sesión para nada, algunas llamadas nuestras para comprobar si existía un atributo en la sesión o no, causaban que se crease la sesión sí o sí. Así que un par de if(request.getSession().getAttribute(…)) que se había colado se cambiaron por if(request.getSession(false)!=null && …) para evitar crear sesiones innecesarias.
  • Debido a que ahora tenemos un cluster con “session failover” aunque a la sesión no le metas nada, ocupa un cierto tamaño que al multiplicarse en número empieza a ser significativo. Debido al mismo factor, las sesiones ocupan espacio en ambos nodos del cluster y no se con “fácilmente recoletables”.
  • Dado que las sesiones son creadas “sin querer”, nadie las cierra y por tanto caducan solas agotando el tiempo máximo de vida sin actividad (session-timeout). Así que durante ese tiempo ocupan espacio en ambos servidores del cluster, inútilmente y encima no se pueden recolectar (GC). 
  • Dado que nuestras aplicaciones son públicas, los buscadores las recorren a menudo y en algunos casos sin reutilizar las cookies entre peticiones, así que nos crean una sesión por petición. 
Afortunadamente, para lo bueno y lo malo usamos nuestro propio framework así que introducir cambios para evitar al máximo la creación de sesiones no fue nada complicado. Así mismo, configurar por web.xml que el time-out de las sesiones es de 1 minuto, por si alguna se escapa, y poner un filtro para las aplicaciones solo públicas que si se crea alguna sesión la cierre al acabar cada petición tampoco fue muy complicado (esto último es por qué algunas librerías usadas en algunas aplicaciones pueden crearte sesiones sin que puedas hacer mucho por evitarlo, o si usas JSP o alguna otra tecnología que las cree alegremente).
El resultado final, probado en un nodo antes que en otro, fue que para el mismo tráfico y uso, todo funcionaba igual pero con 1/3 menos de consumo de memoria. Además, ahora un pico de tráfico público, más difícil de controlar, no nos daría tantos problemas mientras las sesiones están esperando a expirar, como pasaba antes, ya que si se crean, el GC las puede liquidar sin problemas.

La moraleja es que aunque creas que no estas usando sesiones, puede que en realidad sí lo estés haciendo y te estén afectando más de lo que creas.

¡Un saludo y happy coding!
E.J.