Stacktrace

Två bra verktyg för att övervaka sin jvm är VisualVM och JConsole. Båda verktygen är enkla att använda och så länge man vill övervaka en jvm på sin lokala maskin så är det inga konstigheter. Men i verkligheten så vill man förmodligen övervaka någon jvm på någon server och det lär det finnas brandväggar i vägen för att krångla till det ännu mer. När man väl har fått portar öppnade av brandväggsfolket så kanske man tror att det bara är att köra på….men riktigt så enkelt är det inte…om man inte har access mot alla portar vill säga. Låt säga att vi har fått port 8000 öppnad mellan vår lokala dator och servern. Sen så har vi startat vår serverapplikation med följande jvm argument:

-Dcom.sun.management.jmxremote.port=8000
-Dcom.sun.management.jmxremote.authenticate=false
-Dcom.sun.management.jmxremote.ssl=false

Vi försöker sedan ansluta med JConsole

> jconsole theserver:8000

jconsole_fail

Vi får ett meddelande som säger: Connection Failed. Vad är nu detta? Vi vet att porten 8000 är öppen mellan vår dator och servern. Kör man en tcpdump på servern så ser man att det skapas en connection och hela TCP sessionen sätts upp korrekt, men mycket mer än så händer inte… Detta beror på att JMX RMI connectorn som skapas öppnar inte bara port 8000 utan även en port till. Port 8000 används för RMI registret och den andra porten som är slumpmässigt vald av RMI stacken används för att exportera JMX RMI connection objekten. Eftersom vi bara kan kommunicera med servern via port 8000 så lyckas inte JConsole ansluta. Det enda sättet att specificera den andra porten som JMX RMI skall använda är via JMXServiceURL. Vi måste helt enkelt skapa en egen Java Agent eftersom den defaulta inte tillåter oss att speca den andra porten:

package se.cygni;

import java.io.IOException;
import java.lang.management.ManagementFactory;
import java.net.InetAddress;
import java.rmi.registry.LocateRegistry;
import java.util.HashMap;

import javax.management.MBeanServer;
import javax.management.remote.JMXConnectorServer;
import javax.management.remote.JMXConnectorServerFactory;
import javax.management.remote.JMXServiceURL;

public class MyJavaAgent {
    private MyJavaAgent() { }

    public static void premain(
       @SuppressWarnings("unused") String agentArgs)
           throws IOException {

        System.setProperty("java.rmi.server.randomIDs", "true");

        final int port= Integer.parseInt(
                System.getProperty("rmi.agent.port","8000"));
        System.out.println("Create RMI registry on port "+port);
        LocateRegistry.createRegistry(port);

        MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
        HashMap env = new HashMap();

        final String hostname = InetAddress.getLocalHost().getHostName();
        JMXServiceURL url =
            new JMXServiceURL("service:jmx:rmi://"+hostname+
            ":"+port+"/jndi/rmi://"+hostname+":"+port+"/jmxrmi");

        JMXConnectorServer cs =
            JMXConnectorServerFactory.
                newJMXConnectorServer(url, env, mbs);

        System.out.println("Start the RMI connector server on port "
              + port);
        cs.start();
    }
}

Denna klass kompilerar vi och packar ihop i en jar-fil som vi döper till tex myagent.jar. I jar-filens manifestfil lägger vi till föjlande entry:

Premain-Class: se.cygni.MyJavaAgent

Vi flyttar över jar-filen till servern och startar server-applikationen med följande jvm argument:

-javaagent:myagent.jar

Vi försöker på nytt ansluta med JConsole:

> jconsole theserver:8000

jconsole_ok

Så där ja, det gick ju bra till slut, nu kan vi ansulta med VisualVM eller JConsole fastän vi bara har en port öppen i brandväggen.

Kommentarer

Skriv kommentar