h1

Tuning de Glassfish 2.1: el baseline

02/26/2010

Wizdoc [Icon By Buuf]  Tips & Tricks.
Cuando realizamos un tuning o puesta a punto para mejorar el desempeño de una aplicación, muchos de nosotros no sabemos por donde empezar. Con anterioridad hemos visto que lo mejor es iniciar tuning del código aplicativo, pues muchas veces es lo más fácil o que mayor impacto logra en el desempeño de la solución, para después ir bajando a las capas más abstractas de la arquitectura (contenedores de aplicaciones o middleware, luego sistema operativo y finalmente hardware). Ahora bien, con bastante regularidad, la mayoría de los encargados de hacer despliegues de aplicaciones saben qué parámetros modificar para hacer que la Máquina Virtual de Java (JVM) dé lo mejor de sí, pero en cuanto al servidor de aplicaciones, todos se quedan en blanco.

Hace poco ayudé a unos compañeros con este tipo de parámetros, pues su aplicación se caía con tan sólo un par de deployments, marcando el temido java.lang.OutOfMemoryError en muy poco tiempo. Entonces, aquí presento los parámetros más socorridos para mejorar el desempeño de una aplicación, con pequeños ejemplos para el servidor de aplicaciones Glassfish 2.1. No son en absoluto exhaustivos, pero pueden proporcionarnos una línea base a partir de la cual podemos ir trabajando:

1. La versión de la Máquina Virtual de Java

Obviamente es un cliché, pero hemos comprobado la diferencia que puede hacer una versión de Java. Siempre es una buena práctica instalar la última versión en los servidores donde correrá la aplicación. No es necesario contar con el root del servidor si se instala mediante un ejecutable (por ejemplo, j2sdk-1_6_0_18-solaris-sparc.sh) lo que permite instalar una nueva versión de Java sin impactar dominios de aplicaciones desplegados previamente. Primero, hay que instalar la nueva versión de la JVM y luego deberemos cambiar el path desde donde la toma Glassfish:

$GLASSFISH_HOME/config/asenv.conf
propiedad a modificar: AS_JAVA=<java_installations>/j2sdk1.6.0_18

2. Modo de ejecución de la Máquina Virtual de Java

Los servidores Glassfish corren por default en modo "cliente". Esto hace que levanten más rápido en un ambiente de desarrollo, pero esta modalidad de ejecución no es ideal en producción. Adicionalmente, por default la JVM tiene un límite de consumo de memoria de 64 Megabytes. Si nuestro servidor productivo tiene más que eso, lo mejor es incrementar los valores de acuerdo al disponible de memoria con el que contamos:

• Ingresar a http:<hostname>:4848
• Firmarse con usuario/password administrador.
• Click en nodo de application server a editar > JVM Settings > JVM Options.
• Editar las opciones de la JVM o agregar nuevas en el campo de texto correspondiente: -server -Xmx1500m -Xms1500m
• Click en Save del lado derecho.
• Reiniciar el servidor.

En el ejemplo Xms es la cantidad de memoria inicial (aquí se definieron 1500 MB o 1.5 Gigabytes) y Xmx es la cantidad de memoria máxima a usar por la JVM.

3. Número de threads de acceso

Los threads de acceso (acceptor threads) son hilos de ejecución asignados a un socket para que el servidor procese peticiones HTTP. El número por default es uno, pero es mejor designar el número de threads de acuerdo al número de cores (núcleos de procesador) que tiene la máquina donde corre el servidor, usando una proporción de 1 thread por cada 1 a 4 cores. Puede ser necesario probar la aplicación con diferentes parámetros para encontrar el óptimo. Ejemplo:

Una M3000 con 2 procesadores dual core puede tener un mínimo de (2 CPUs x 2 dual cores / 4 threads por core) = 1 thread o un máximo de (2 CPUs x 2 dual cores / 1 thread por core) = 4 threads.

Para modificar los valores en Glassfish, seguimos los siguientes pasos:

• Ingresar a http:<hostname>:4848
• Firmarse con usuario/password administrador.
• Click en nodo de application server a editar: Configuration > HTTP Service > HTTP Listeners
• Click en http-listener-1
• Editar el campo Acceptor Threads en la sección Advanced
• Click en Save del lado derecho.
• Reiniciar el servidor.

4. Número de threads de procesamiento

Los threads de procesamiento son los que ejecutan las peticiones HTTP. El default es 5, pero debe modificarse de acuerdo al número de cores que posee la máquina en proporción 1 a 1. Si la aplicación ocupa mucho uso de disco (I/O), es recomendable que el valor sea multiplicado por dos. Ejemplo:

Una M3000 con 2 dual cores puede tener 2 CPUs x 2 dual cores = 4 threads; si la aplicación usa mucho I/O, el número se multiplica por dos, quedando en 8 threads.

Nuevamente, para implementar este cambio, podemos seguir los siguientes pasos:

• Ingresar a http:<hostname>:4848
• Firmarse con usuario/password administrador.
• Click en nodo de application server a editar: Configuration > HTTP Service > Tab RequestProcessing
• Cambiar el valor Thread Count al número correspondiente.
• Click en Save del lado derecho.
• Reiniciar el servidor.

5. Subsistema Keep-alive

El keep-alive se utiliza para optimizar el uso de red. Normalmente, una petición HTTP requiere abrir la conexión – enviar datos – cerrar la conexión. Pero si una misma conexión envía información en diferentes tiempos, ¿no sería mejor dejarla siempre abierta para no pasar por el trabajoso proceso de abrirla? Siempre se recomienda cambiar el número de threads a 1 por cada 8 cores en la máquina para mejorar el uso de estos recursos. Ejemplo:

Una M3000 con 2 dual cores puede tener 2 CPUs x 2 cores = 4; por default debe tener 1 thread asignado. Si dicha M3000 tuviera 4 CPUs quad core, tendría 2 CPUs x 4 cores = 16; un default de 2 threads asignados.

• Ingresar a http:<hostname>:4848
• Firmarse con usuario/password administrador.
• Click en nodo de application server a editar: Configuration > HTTP Service > Tab KeepAlive
• Editar campo thread-count.
• Click en Save del lado derecho.
• Reiniciar el servidor.

6. Cacheo de archivos estáticos

En caso de utilizar muchos archivos estáticos en la aplicación (HTML, imágenes) es necesario cambiar los valores por default de acuerdo al tamaño promedio de estos archivos. Nota: es necesario calcular estos valores previamente a su ingreso al servidor para compararlo con el default, que en la mayoría de las veces es más que suficiente. Esto porque el incremento de cache aumenta los requerimientos de memoria llegando incluso a bloquear el servidor. Ejemplo:

La aplicación tiene 2,000 HTMLs estáticos con un tamaño promedio de 600 KB cada uno y 4,000 imágenes con un tamaño promedio de 200 KB cada una, entonces se requerirían los siguientes parámetros:

Globally = Enabled
MaxFilesCount = 6000
MediumFileSizeLimit = 614400
MediumFileSize = 1228800000 (= 614,400 bytes x 2,000 = 1.14 GigaBytes)
SmallFileSizeLimit = 204800
SmallFileSize = 819200000 (= 204,800 bytes x 4,000 = 781.25 MegaBytes)
FileCacheEnabling = On

• Ingresar a http:<hostname>:4848
• Firmarse con usuario/password administrador.
• Click en nodo de application server a editar: Configuration > HTTP Service > Tab HTTP File Cache
• Habilitar cache globalmente (primer select box)
• Cambiar tamaños de propiedades relacionadas (ver ejemplo)
• Click en Save del lado derecho.
• Reiniciar el servidor.

7. Deshabilitar log de acceso

Siempre se recomienda deshabilitar el log de acceso HTTP en un despliegue productivo, especialmente si hay un web server como front/proxy para la plataforma – pues ya tendrá su propio log – o existe mucho I/O por parte de la aplicación que estaría compitiendo por uso de disco.

• Ingresar a http:<hostname>:4848
• Firmarse con usuario/password administrador.
• Click en nodo de application server a editar: Configuration > HTTP Service
• Deshabilitar casilla Access Logging
• Click en Save del lado derecho.
• Reiniciar el servidor.

8. default-web.xml

Una vez que hayamos decidido subir el desarrollo a producción, ya no es necesario que el servidor esté corriendo un demonio que verifique si los JSP han cambiado para recompilarlos. Por otro lado, cuando se compila un JSP, siempre se requiere usar el método toCharArray para parsear cadenas estáticas, usadas normalmente en etiquetas HTML y JSP. Si desde el principio indicamos que estas cadenas deben ser arreglos de caracteres, evitamos la llamada constante a dicho método.

Para modificar este comportamiento, editamos el archivo default-web.xml, agregando las siguientes etiquetas al cuerpo del XML:

<init-param>
  <param-name>development</param-name>
  <param-value>false</param-value>
</init-param>
<init-param>
  <param-name>genStrAsCharArray</param-name>
  <param-value>true</param-value>
</init-param>

El archivo se encuentra generalmente localizado en $GLASSFISH_HOME/domains/domain1/config/default-web.xml

9. Configuración de JDBC

Siempre es deseable optimizar las conexiones de JDBC. Agregando los siguientes parámetros a la configuración de la misma en Glassfish, logramos parte del cometido:

Parámetros para Oracle:

ImplicitCachingEnabled=true
MaxStatements=200

Parámetros para MySQL:

cachePrepStmts=true
prepStmtCacheSize=512
useServerPreparedStmts=false

Cabe mencionar que estos parámetros funcionan sólo si en la aplicación se utilizan PreparedStatements como método de ejecución de queries a la base de datos.

• Ingresar a http:<hostname>:4848
• Firmarse con usuario/password administrador.
• Click en nodo de application server a editar: Resource > JDBC > Connection pools
• Click en <poolName>.
• Click en tab Additional Properties.
• Click en Add Properties.
• Agregar propiedades en forma Name=Value.
• Click en Save del lado derecho.
• Reiniciar el servidor.

10. Configuración del Garbage Collector

En términos generales, el Garbage Collector es una de las piezas de la JVM que más cambios ha sufrido desde que Java hizo su aparición. Dicho componente permite recuperar memoria una vez un objeto ha sido descartado; sin embargo el demonio del Garbage Collector requiere de cierto tuning pues en ambientes productivos puede saturarse, "deteniendo el mundo" constantemente mientras ejecuta la depuración de objetos. Libros enteros y miles de artículos han sido escritos para realizar este tipo de optimización, pero un par de pequeños cambios pueden ser más que suficientes.

Por default, se usa el modo "serial" de recolección, pero esto sólo sirve en máquinas con un solo CPU. Para servidores de aplicaciones con 2 o más cores, es recomendable seleccionar el modo "paralelo" mediante la opción -XX:+UseParallelGC. Por otro lado, éste se puede utilizar en modalidad de "pequeños impulsos frecuentes" (Concurrent Mark Sweep – CMS) mediante la opción -XX:+UseConcMarkSweepGC. Esta bandera disminuye un poco el rendimiento del sistema a cambio de no congelar la aplicación cada que se llena el GC.

• Ingresar a http:<hostname>:4848
• Firmarse con usuario/password administrador.
• Click en nodo de application server a editar > JVM Settings > JVM Options.
• Editar las opciones de la JVM o agregar nuevas en el campo de texto correspondiente: -XX:+UseParallelGC o en su caso, -XX:+UseConcMarkSweepGC
• Click en Save del lado derecho.
• Reiniciar el servidor.

6 comentarios

  1. Muchas gracias por los consejos. Vengo de aplicaciones desktop e hice el salto a aplicaciones web con Java y andaba perdido con la configuración del GlassFish.

    Saludos y gracias de nuevo.


  2. Saludos te felicito por el articulo, deseo ponerme en contacto con tu persona atraves de un correo.


  3. Muchas gracias. Muy útil tu artículo. Me he dado una vuelta por tu blog y compruebo que compartimos también la astronomía como parte de nuestras inquietudes.

    Un cordial saludo.


  4. muy buen articulo…
    me ha servido de mucho…
    saludos meyquel


  5. Muchas gracias.. ha sido de gran ayuda!



Responder

Introduce tus datos o haz clic en un icono para iniciar sesión:

Logo de WordPress.com

Estás comentando usando tu cuenta de WordPress.com. Cerrar sesión / Cambiar )

Imagen de Twitter

Estás comentando usando tu cuenta de Twitter. Cerrar sesión / Cambiar )

Foto de Facebook

Estás comentando usando tu cuenta de Facebook. Cerrar sesión / Cambiar )

Google+ photo

Estás comentando usando tu cuenta de Google+. Cerrar sesión / Cambiar )

Conectando a %s

A %d blogueros les gusta esto: