Mostrando entradas con la etiqueta jmx. Mostrar todas las entradas
Mostrando entradas con la etiqueta jmx. Mostrar todas las entradas

jueves, octubre 18, 2007

Una nueva utilidad para monitorizar las máquinas virtuales Java

jueves, octubre 18, 2007 por Martín

Ayer descubrí VisualVM que es una verdadera joya. Se trata de una aplicación que es capaz de descubrir los diferentes procesos Java que se ejecutan en una máquina, tanto local como remota, y que te ofrece montones de información interna sobre como están funcionando estos procesos.



VisualVM está preparado para funcionar sobre todo con Java 6. En realidad, es capaz de funcionar con Java 5 o Java 1.4, pero con estas dos últimas máquinas virtuales no obtendremos realmente mucha información.

Ahora bien, si estamos utilizando Java 6 entonces esta herramienta se convierte en fundamental. Ofrece muchísima información. Nada que no se pueda obtener con otras herramientas como jhat o jstat, pero lo mejor es que viene de forma gráfica y encima no hay que configurar nada en la máquina virtual, simplemente te conectas a ella y listo.



A mi lo que más me ha gustado es la facilidad para obtener un volcado de memoria o de threads simplemente con un click. Que maravilla. No os imagináis lo útil que es esto cuando estás ejecutando un Applet ya que normalmente suele ser bastante engorroso el andar habilitando JMX para usar JConsole, o habilitando agentes en la configuración de tu Java plugin, etc. etc. Por cierto, que esta herramienta es muy similar en funcionamiento y concepto a Mission Control de BEA de la que ya hablé hace tiempo.

¿Algún otro truquillo/herramienta que utilicéis?

jueves, junio 07, 2007

Quiero mi consola de administración

jueves, junio 07, 2007 por Martín

Uno de los artefactos por el que claramente apuesta el libro que recomendaba hace unos días es el ofrecer una consola de administración para las aplicaciones.

La idea, con la que estoy completamente de acuerdo, es muy sencilla. Lo malo es que a pesar de estar totalmente de acuerdo, siempre he acabado por no hacerlo, en cualquiera de los trabajos en los que he estado. Shame on me! La verdad es que al final he caido siempre en la vieja práctica de crear un interfaz de usuario muy amigable para administrar el sistema. Sin embargo, el hacer esto tiene normalmente varios problemas:

  • Suelen ser aplicaciones Swing o web que simplemente acceden directamente a los servicios. Por lo tanto si los servicios cambian las aplicaciones deben de cambiar.
  • Tienen asociado un coste de mantenimiento importante. Cada vez que se cambia algo (punto anterior) y cada vez que se añade una nueva funcionalidad, es necesario el crear nuevas páginas o ventanas con nuevos interfaces de usuario para administrar esta parte nueva del sistema.
  • Es una solución propensa a errores. Incluso cuando tu backend esté perfectamente probado, pasas a lidiar con todos los errores asociados a un framework de creación de interfaces de usuario: Swing, Java Server Faces, Seam, Struts, SWT/JFace, ...
  • Hacer pruebas es complicado. Probar los servicios es fácil, simplemente un conjunto de tests unitarios y tests de integración y listo. Pero si le vas a ofrecer un interfaz de usuario de administración al usuario final entonces tendrás que probarlo. Y esto nuevamente tiene asociados unos costes importantes.
  • No son scriptables. Este tipo de UIs están bien para usuarios, pero no para administradores, que como bien dice el nombre al final deberían realmente ser las personas que utilicen las interfaces de administración. Un administrador va a preferir cien mil millones de veces algo con lo que pueda hacer scripts y automatizar tareas que una interfaz de usuario que por muy bonita que sea le obligue a crear uno a uno todos los usuarios del sistema.

    Esta probablemente sea una de las razones más poderosas para basar el sistema de administración algún sistema de comandos que sea scriptable. Te permite automatizar tareas tediosas, crear tus propios scripts de mantenimiento, replicar sistemas de manera sencilla, y realizar un sinfín de tareas que de otro modo serían demasiado complicadas.


En fin, estas son algunas razones y supongo que os darán una idea de los problemas asociados. Yo mismo, ahora que estoy peleando para sacar una versión de actualización de jLibrary, me encuentro con que sufro para mantener el interfaz de usuario. Para mi, ya no es sencillo juguetear con pantallas de administración como esta:



Sí, es una pantalla muy bonita, pero mantenerla me implica todo lo explicado anteriormente: Mantener los widgets, lidiar con cosas como "que si migro a Eclipse 3.2 entonces han cambiado los widgets y tengo que modificar el código para que las listas salgan con borde porque ahora ya no salen", asegurarme de que no hay errores, etc. Si por el contrario tuviese una simple consola de comandos con funciones como crearUsuario, crearRol, añadirUsuarioAGrupo todo se limitaría simplemente a llamar a esos métodos y punto.

Probablemente una de las mejores formas de crear una consola de administración es simplemente codificar una pequeña aplicación que exponga todos los métodos de nuestro servicio de administración en forma de comandos. Hay una ventaja adicional asociada a esto, que es que una vez que tengamos diferentes scripts creados, estos servirán como tests de integración del sistema por lo que realmente se están matando dos pájaros de un tiro: tests de integración y administración.

Ahora bien, en caso de tener tiempo (y ahora entro en la parte concreta de Java) probablemente nos convenga seguir la aproximación que servidores de aplicaciones como BEA WebLogic o IBM WebSphere siguen. Esto es, envolver nuestro sistema de administración en una serie de MBeans que sean los que expongan las operaciones. Estos MBeans invocarán a nuestro sistema de administración. Una vez se tiene esto, se crea una consola de administración sencilla utilizando algún lenguaje dinámico (por ejemplo WebLogic utiliza Jython; WebSphere jacl and jython) que simplemente ejerza de wrapper entre el usuario y los MBeans, por ejemplo exponiendo todos los MBeans en forma de objetos python que el usuario pueda invocar.

Si se consigue un sistema como este nos encontraríamos ante el entorno ideal de administración. El paso siguiente es aprovechar esos MBeans para exponer el entorno de administración en otros formatos, pues por ejemplo una consola web (como hacen WebLogic y WebSphere), o incluso aplicaciones de escritorio.

Una ventaja adicional a todo esto es que los MBeans se pueden exponer también hacia sistemas de gestión SNMP utilizando alguna librería de transformación JMX-SNMP, de modo que el estado de nuestro sistema de administración puede ser monitorizado con cualquier herramienta que soporte SNMP.

En fin, aquí dejo un diagramilla que me he currado mientras escribía esto.

viernes, abril 06, 2007

Instrumentando clases Java con anotaciones y class weaving

viernes, abril 06, 2007 por Martín

Hace unos meses estuve experimentando un poco con anotaciones y he decidido a escribir aquí el resultado, como referencia para que no se me olvide, y por si de paso a alguien le interesa, así que esta entrada va a ser bastante técnica. Empiezo...

La monitorización de sistemas es algo realmente fundamental, al menos para aplicaciones de medio y gran tamaño. Hoy en día, sin un sistema que nos permita examinar en tiempo real el estado de nuestras aplicaciones, se hace realmente complicado el encontrar errores, especialmente en sistemas distribuidos. Por poner un ejemplo, si tenemos un sistema de caché que funciona en cluster puede ser muy importante el saber en cada momento cual es el contenido de la caché, el saber si unas cachés están más llenas que otras, el ser capaz de ejecutar operaciones sobre la caché, etc.

En Java, JMX (en .NET ¿WMI?) permite la instrumentación de sistemas y la obtención de datos en tiempo real a través de herramientas como jconsole. Un libro que ya he recomendado en algún otro post, Pro Java EE 5 Performance Management and Optimization tiene varios capítulos donde recalca la importancia de instrumentar nuestro código en sistemas distribuidos complejos y pone algunos ejemplos con JMX.

La primera opción que nos puede venir a la cabeza a la hora de instrumentar nuestro código es simplemente crear nuestros managed beans y añadir el código de instrumentación directamente a nuestros proyectos:
MBeanServer mbs = ManagementFactory.getPlatformMBeanServer(); 
ObjectName name = new ObjectName("com.example.mbeans:type=Hello"); 
Hello mbean = new Hello(); 
mbs.registerMBean(mbean, name); 


Esta opción es sostenible y perfectamente justificable para proyectos pequeños. En cuanto los proyectos son de mayor calibre e involucran una cantidad considerable de componentes y desarrolladores es mejor buscar una solución más mantenible.

La alternativa natural sería crear un framework de monitorización que centralize toda instrumentación del proyecto. Esta es una opción muy recomendable que puede perfectamente cubrir todas nuestras necesidades.

Una vez que tenemos creado nuestro framework de monitorización, lo único que tenemos que hacer es decirle a nuestros desarrolladores como utilizarlo, y listo. ¿En serio? Pues la verdad es que depende mucho de nuestros desarrolladores, del movimiento que haya en la empresa (¿están continuamente saliendo y entrando nuevos programadores?), y de nuestra capacidad de formar a los miembros más junior del equipo. Porque aunque el framework de monitorización nos ahorrará mucho trabajo, lo cierto es que si nuestros programadores no saben como utilizarlo, entonces a la larga puede ser una solución menos productiva. Por ejemplo nos podemos encontrar con que un desarrollador ha colocado objetos gigantescos en memoria, que otro ha utilizado el framework para exponer operaciones que exponen nuestro sistema, que otra persona ha ejecutado código muy costoso dentro de los managed beans, o en resumen que cada persona utiliza el framework de una manera diferente.

Una solución a este problema es hacer mucho más fácil y estándar el uso de nuestro framework de monitorización, y ahí es donde entra en juego el concepto de las anotaciones. Si añadimos anotaciones al framework, los desarrolladores sólo tendrán por ejemplo que etiquetar sus clases, métodos o atributos, y nuestro framework de monitorización realizará todo el trabajo sucio: crear los managed beans, gestionar su registro en la plataforma de monitorización, controlar el árbol de nombres, o incluso operaciones más avanzadas como filtrar los componentes que se monitorizan y los que no se monitorizan.

Una opción muy parecida es la que ofrece Spring JMX o incluso de algún modo lo que ofrece el JDK 6. El problema del segundo es que el soporte no es demasiado avanzado, simplemente es un conjunto de anotaciones para no tener que extender clases o implementar interfaces. El problema del primero es que te obliga a utilizar Spring, algo que quizás no sea posible o simplemente no se quiera añadir por cualquier otra razón.

En los siguientes pasos voy a mostrar una aproximación al uso de anotaciones para este escenario. Lo primero es crear las anotaciones. En este caso serán muy simples a modo de ejemplo.

Anotación para cualquier clase que sea un MBean:
@Retention(RetentionPolicy.RUNTIME)  
@Target(ElementType.TYPE)
public @interface MBean {
String name();
String root();
} 


Anotación para un atributo instrumentado:

@Retention(RetentionPolicy.RUNTIME)  
@Target(ElementType.FIELD)
public @interface MBeanAttribute { 
String name();
} 


Anotación para un método instrumentado:
@Retention(RetentionPolicy.RUNTIME)  
@Target(ElementType.METHOD)
public @interface MBeanMethod { 

}


Una vez definidas las anotaciones ya se podría marcar cualquier clase Java como un MBean. El siguiente código muestra un servicio instrumentado:

@MBean (root="org.test:",name="TestService")
public class TestService {

public static boolean testBoolean = true;
public static String testString = "Test service";

@MBeanAttribute(name="stringAttribute")
public String stringAttribute;
private long lastLong;

public String getStringAttribute() {
return testString;
} 

@MBeanMethod()
public boolean getTestState() {

return testBoolean;
}

@MBeanMethod()
public long customOperation() {

// do some monitoring calcs here
lastLong = Math.round(Math.random()*1000);
return lastLong;
}

public void nonMBeanMethod() {}

public long getLastLong() {
return lastLong;
}
}


Hasta aquí todo bien, pero el que todavía siga leyendo se preguntará... ¿y ahora qué? La idea original era ser capaz de monitorizar nuestro sistema, es decir, cualquier clase Java, sin tener que llenarla de código de monitorización ni obligar a nuestros programadores a utilizar un framework, pero es que hasta este momento el código de monitorización está en nuestro framework imaginario. Así que, ¿cómo se enlaza dicho framework y estas clases Java?

Para hacer eso inyectaremos directamente el código de instrumentación dentro de las clases Java. Esto es lo que normalmente se conoce como weaving, un término relacionado con la programación orientada a aspectos (de hecho todo lo que estoy mostrando aquí se podría realizar también con AOP). Para inyectar el código en este ejemplo utilizaré el framework javassist ya que es bastante descriptivo, pero otros frameworks podrían ser más útiles por ser de bajo nivel como BCEL o ASM. Migrar el ejemplo a estos entornos puede quedar como ejercicio :)

El proceso de weaving de nuestras clases es el más laborioso y la verdad es que resulta difícil poner el código fuente aquí. Voy a escribir a continuación el pseudocódigo del proceso de instrumentación, y podéis leer directamente el código fuente en el enlace de descarga que pongo al final.
  1. Escanear una clase en busca de la anotación @MBean.
  2. En caso de encontrar la anotación, buscar todos los métodos (anotación @MBeanMethod), y los atributos (anotacion @MBeanAttribute) expuestos por la clase que hemos encontrado.
  3. Crear una interfaz utilizando javaassist. Esta interfaz contendrá todos los métodos instrumentados que hemos encontrado, más una serie de métodos get que se generarán para cada uno de los atributos instrumentados (otra opción sería olvidarse de la anotación de atributos y asumir que todo método get que encontremos expondrá un atributo).
  4. A continuación, crear una nueva clase que actuará de proxy e implementará la interfaz que hemos generado en el paso anterior. Esta clase proxy extenderá a StandardMBean, MXBean, o cualquier otra clase que creemos que sea un MBean y que contenga métodos genéricos. Así, en todos los proxies que creemos, tendremos una serie de funcionalidad común.
  5. Crear el constructor del proxy, de forma que se guarde una referencia local al objeto que realmente se quiere monitorizar (esa referencia podría ser una soft o weak reference). El constructor también deberá registrar el managed bean en nuestro framework una vez que ha sido creado.
  6. Implementar todos los métodos de la interfaz en la clase proxy que hemos creado. Estos métodos simplemente delegarán las llamadas sobre el objeto original que se ha recibido en el constructor del proxy.
  7. Por último, modificar el constructor (o los constructores) del objeto original a monitorizar, de forma que cuando se cree una instancia de dicho objeto se cree también un proxy MBean.
El siguiente diagrama muestra como funciona el proceso de weaving. Como lanzar este proceso queda a vuestra elección. Podéis crear un plug-in de maven que simplemente ejecute este algoritmo como parte del proceso de build, o incluso podéis ir más allá y crear un ClassLoader que realize el weaving en tiempo de ejecución cada vez que una clase se carga y registrar ese ClassLoader en vuestro servidor de aplicaciones favorito. La ventaja de este método que he expuesto es que se lo podéis aplicar a cualquier código existente sin tener que añadir ningún tipo de dependencia, tan sólo anotando las clases que ya existen. Pero ojo, también hay alguna desventaja. La principal es que el proceso de weaving modifica las clases originales, con lo que quizás no podréis depurarlas fácilmente. Esto no lo he confirmado aunque probablemente dependa de como funcione javassist. Teóricamente, las modificaciones en el bytecode no tienen porque alterar el proceso de depuración de un programa, aunque añadan nuevo código. Esto es exactamente lo que hacen productos como terracotta. Un JSR específica todo el proceso que garantiza la depuración aunque se modifiquen los bytecodes, pero no recuerdo ahora cual era. Como he comentado anteriormente el código fuente de la parte de weaving es demasiado extenso como para colocarlo aquí. He comprimido todo el código fuente y lo he añadido al final de la entrada. El proyecto ha sido creado con maven 2, así que lo necesitaréis para descargarse las dependencias. Hay un unit test que podéis ejecutar. El test ejecutará todo el weaving para el servicio que he puesto de ejemplo en este artículo y comprobará que los MBeans se registran, que los atributos son accesibles y que se pueden ejecutar los métodos. Podéis ver a continuación varias capturas de jconsole mostrando el servicio anterior.
Si miráis detenidamente la tercera captura de pantalla veréis como el managed bean no es el objeto sino que es el proxy que está delegando todas las operaciones sobre el objeto real. Pues nada, espero que os haya parecido interesante esta idea, sobre anotaciones y weaving de clases. Probablemente el modelo se pueda llevar mucho más allá, como han hecho en terracotta, pero eso ya normalmente dependerá de nuestros recursos y del problema que queramos resolver. Cualquier comentario, ya sabéis. Descárgate el código fuente del ejemplo.