Llamar al método JMX MBean desde un script de shell

98

¿Hay alguna biblioteca que me permita llamar a un método JMX MBean desde un script de shell? Exponemos algunas operaciones / comandos de administración a través de JMX, y podríamos hacer que nuestros administradores usen JConsole o VisualVM, pero es mejor dejar algunas tareas a la automatización. En esa automatización, nos gustaría poder llamar a un método JMX MBean en nuestro servidor en ejecución, preferiblemente desde un script de shell.

Dougnukem
fuente

Respuestas:

106

Están disponibles las siguientes utilidades JMX de línea de comandos:

  1. jmxterm : parece ser la utilidad con más funciones.
  2. cmdline-jmxclient : utilizado en el proyecto WebArchive parece muy básico (y parece que no hay desarrollo desde 2006)
  3. Groovy script y JMX : proporciona una funcionalidad JMX realmente poderosa, pero requiere una configuración de biblioteca maravillosa y de otro tipo.
  4. Funcionalidad de línea de comandos de JManage - (el inconveniente es que requiere un servidor JManage en ejecución para los comandos de proxy)

Ejemplo de Groovy JMX:

import java.lang.management.*
import javax.management.ObjectName
import javax.management.remote.JMXConnectorFactory as JmxFactory
import javax.management.remote.JMXServiceURL as JmxUrl

def serverUrl = 'service:jmx:rmi:///jndi/rmi://localhost:9003/jmxrmi'
String beanName = "com.webwars.gameplatform.data:type=udmdataloadsystem,id=0"
def server = JmxFactory.connect(new JmxUrl(serverUrl)).MBeanServerConnection
def dataSystem = new GroovyMBean(server, beanName)

println "Connected to:\n$dataSystem\n"

println "Executing jmxForceRefresh()"
dataSystem.jmxForceRefresh();

Ejemplo de cmdline-jmxclient:

Si tienes un

  • MBean: com.company.data:type=datasystem,id=0

Con una Operación llamada:

  • jmxForceRefresh ()

Luego, puede escribir un script bash simple (suponiendo que descargue cmdline-jmxclient-0.10.3.jar y lo ponga en el mismo directorio que su script):

#!/bin/bash

cmdLineJMXJar=./cmdline-jmxclient-0.10.3.jar
user=yourUser
password=yourPassword
jmxHost=localhost
port=9003

#No User and password so pass '-'
echo "Available Operations for com.company.data:type=datasystem,id=0"
java -jar ${cmdLineJMXJar} ${user}:${password} ${jmxHost}:${port} com.company.data:type=datasystem,id=0

echo "Executing XML update..."
java -jar ${cmdLineJMXJar} - ${jmxHost}:${port} com.company.data:type=datasystem,id=0 jmxForceRefresh
Dougnukem
fuente
jmxterm no parece funcionar en Java 7 bugs.launchpad.net/jmxterm/+bug/942693
artbristol
19

Desarrollé jmxfuse que expone JMX Mbeans como un sistema de archivos Linux FUSE con una funcionalidad similar a / proc fs. Se basa en Jolokia como puente hacia JMX. Los atributos y operaciones están expuestos para lectura y escritura.

http://code.google.com/p/jmxfuse/

Por ejemplo, para leer un atributo:

me@oddjob:jmx$ cd log4j/root/attributes
me@oddjob:jmx$ cat priority

escribir un atributo:

me@oddjob:jmx$ echo "WARN" > priority

para invocar una operación:

me@oddjob:jmx$ cd Catalina/none/none/WebModule/localhost/helloworld/operations/addParameter
me@oddjob:jmx$ echo "myParam myValue" > invoke
Alastair McCormack
fuente
12

El complemento Syabru Nagios JMX está destinado a ser utilizado desde Nagios, pero no requiere Nagios y es muy conveniente para el uso de la línea de comandos:

~$ ./check_jmx -U service:jmx:rmi:///jndi/rmi://localhost:1099/JMXConnector --username myuser --password mypass -O java.lang:type=Memory -A HeapMemoryUsage -K used 
JMX OK - HeapMemoryUsage.used = 445012360 | 'HeapMemoryUsage used'=445012360;;;;
Austin Mills
fuente
Esto es genial y muy rápido. Aproximadamente 0,3 segundos para devolver un valor frente a 3 segundos para jmxterm
sivann
9

Potencialmente, es más fácil escribir esto en Java

import javax.management.*;
import javax.management.remote.*;

public class JmxInvoke {

    public static void main(String... args) throws Exception {

        JMXConnectorFactory.connect(new JMXServiceURL(args[0]))
            .getMBeanServerConnection().invoke(new ObjectName(args[1]), args[2], new Object[]{}, new String[]{})


    }

}

Esto se compilaría en una sola .clase y no necesita dependencias en el servidor ni en ningún paquete complicado de Maven.

llámalo con

javac JmxInvoke.java
java -cp . JmxInvoke [url] [beanName] [method]
Teknopaul
fuente
4

Un poco arriesgado, pero puede ejecutar un comando curl POST con los valores del formulario de la consola JMX, su URL y autenticación http (si es necesario):

curl -s -X POST --user 'myuser:mypass'
  --data "action=invokeOp&name=App:service=ThisServiceOp&methodIndex=3&arg0=value1&arg1=value1&submit=Invoke"
  http://yourhost.domain.com/jmx-console/HtmlAdaptor

Atención: el índice del método puede cambiar con cambios en el software. Y la implementación del formulario web podría cambiar.

Lo anterior se basa en la fuente de la página de servicio JMX para la operación que desea realizar:

http://yourhost.domain.com/jmx-console/HtmlAdaptor?action=inspectMBean&name=YourJMXServiceName

Fuente del formulario:

form method="post" action="HtmlAdaptor">
   <input type="hidden" name="action" value="invokeOp">
   <input type="hidden" name="name" value="App:service=ThisServiceOp">
   <input type="hidden" name="methodIndex" value="3">
   <hr align='left' width='80'>
   <h4>void ThisOperation()</h4>
   <p>Operation exposed for management</p>
    <table cellspacing="2" cellpadding="2" border="1">
        <tr class="OperationHeader">
            <th>Param</th>
            <th>ParamType</th>
            <th>ParamValue</th>
            <th>ParamDescription</th>
        </tr>
        <tr>
            <td>p1</td>
           <td>java.lang.String</td>
         <td> 
            <input type="text" name="arg0">
         </td>
         <td>(no description)</td>
        </tr>
        <tr>
            <td>p2</td>
           <td>arg1Type</td>
         <td> 
            <input type="text" name="arg1">
         </td>
         <td>(no description)</td>
        </tr>
    </table>
    <input type="submit" value="Invoke">
</form>
BBay
fuente
Lo implementé de esta manera desde Java usando un HttpURLConnectiony puedo confirmar que funciona. (por cierto, no submit=Invokees necesario)
tom
¿Es posible describir cómo funciona? Quiero decir, por defecto, jmx usa rmi, y ahí lo veo http. ¿Significa que el servidor debe configurarse para admitir solicitudes jmx http?
Psychozoic
3

Eche un vistazo a JManage . Es capaz de ejecutar métodos MBean y obtener / establecer atributos desde la línea de comandos .

ChssPly76
fuente
El único inconveniente es usar la utilidad de línea de comandos que requiere que JManage se esté ejecutando para los comandos de proxy en sus servidores JMX. Preferiría un enfoque más ligero directamente al servidor JMX.
Dougnukem
3

Es posible que también desee echar un vistazo a jmx4perl . Proporciona acceso sin Java a los MBeans de un servidor Java EE remoto. Sin embargo, es necesario instalar un pequeño servlet de agente en la plataforma de destino, que proporciona un acceso JMX tranquilo a través de HTTP con una carga útil JSON. (La versión 0.50 agregará un modo sin agente al implementar un proxy JSR-160).

Las ventajas son los tiempos de inicio rápidos en comparación con el lanzamiento de una JVM de Java local y la facilidad de uso. jmx4perl viene con un conjunto completo de módulos Perl que se pueden usar fácilmente en sus propios scripts:

use JMX::Jmx4Perl;
use JMX::Jmx4Perl::Alias;   # Import certains aliases for MBeans

print "Memory Used: ",
      JMX::Jmx4Perl
          ->new(url => "http://localhost:8080/j4p")
          ->get_attribute(MEMORY_HEAP_USED);

También puede usar alias para combinaciones comunes de MBean / Atributo / Operación (por ejemplo, para la mayoría de MXBeans). Para características adicionales (Nagios-Plugin, acceso similar a XPath a tipos de atributos complejos, ...), consulte la documentación de jmx4perl.

Roland Huß
fuente
1

La respuesta de @Dougnukem me ayudó mucho. He adoptado el enfoque Groovy (usando groovy 2.3.3).

Hice algunos cambios en el código Dougnukem. Esto funcionará con Java 7 e imprimirá dos atributos en la salida estándar cada 10 segundos.

        package com.my.company.jmx
        import groovy.util.GroovyMBean;
        import javax.management.remote.JMXServiceURL
        import javax.management.remote.JMXConnectorFactory
        import java.lang.management.*

            class Monitor {
                static main(args) {
                    def serverUrl = 'service:jmx:rmi:///jndi/rmi://localhost:5019/jmxrmi'
                    String beanName = "Catalina:type=DataSource,class=javax.sql.DataSource,name=\"jdbc/CommonDB\""
                    println  "numIdle,numActive"

                    while(1){
                        def server = JMXConnectorFactory.connect(new JMXServiceURL(serverUrl))
                       //make sure to reconnect in case the jvm was restrated 
                        server.connect()
                        GroovyMBean mbean = new GroovyMBean(server.MBeanServerConnection, beanName)
                        println  "${mbean.numIdle},${mbean.numActive}"
                        server.close()
                        sleep(10000)
                    }

                }
            }

Compile este código en un frasco usando maven-compiler-plugin para que no requiera una instalación maravillosa, solo groovy-all.jar. A continuación se muestra la definición y dependencia del complemento relevante.

   <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <compilerId>groovy-eclipse-compiler</compilerId>
                    <source>1.7</source>
                    <target>1.7</target>
                </configuration>
                <dependencies>
                    <dependency>
                        <groupId>org.codehaus.groovy</groupId>
                        <artifactId>groovy-eclipse-compiler</artifactId>
                        <version>2.8.0-01</version>
                    </dependency>
                    <dependency>
                        <groupId>org.codehaus.groovy</groupId>
                        <artifactId>groovy-eclipse-batch</artifactId>
                        <version>2.3.4-01</version>
                    </dependency>
                </dependencies>
            </plugin>
        </plugins>
    </build>
    <dependencies>
        <dependency>
            <groupId>org.codehaus.groovy</groupId>
            <artifactId>groovy-all</artifactId>
            <version>2.4.3</version>
        </dependency>
    </dependencies>

Envuélvalo con un murciélago o un caparazón e imprimirá los datos en la salida estándar.

Haim Raman
fuente
0

No estoy seguro acerca de un entorno similar a bash. Puede probar algunos programas contenedores simples en Java (con argumentos de programa) que invocan sus MBeans en el servidor remoto. Luego puede llamar a estos contenedores desde el script de shell

Si puede usar algo como Python o Perl, es posible que le interese JSR-262, que le permite exponer operaciones JMX a través de servicios web. Esto está programado para incluirse en Java 7, pero es posible que pueda utilizar un candidato de lanzamiento de la implementación de referencia

Kevin
fuente