¿Qué es Arthas y Para Qué Sirve?
Arthas es una potente herramienta de diagnóstico de código abierto desarrollada por Alibaba. Diseñada específicamente para aplicaciones Java, Arthas permite a los desarrolladores solucionar problemas en tiempo real en entornos de producción, proporcionando capacidades de depuración avanzadas sin necesidad de reiniciar la aplicación ni modificar el código fuente.
Esta herramienta resulta invaluable en diversas situaciones, tales como:
- Identificar el JAR de origen de una clase o resolver excepciones relacionadas con la carga de clases.
- Verificar la ejecución de cambios de código en un entorno en vivo sin necesidad de un redepliegue.
- Depurar problemas complejos en producción donde la depuración remota tradicional no es factible.
- Investigar datos específicos de un usuario o escenarios de errores que solo se manifiestan en el entorno productivo.
- Obtener una visión general del estado del sistema, incluyendo el monitoreo en tiempo real del JVM.
- Localizar cuellos de botella de rendimiento y generar perfiles de llama para optimización.
Arthas es compatible con JDK 6+ y puede ejecutarse en Linux, macOS y Windows. Su interfaz de línea de comandos ofrece una experiencia de usuario eficiente, complementada con autocompletado de comandos para facilitar la navegación y el diagnóstico.
Instalación de Arthas
Instalación Rápida con arthas-boot (Recomendado)
La manera más sencilla y recomendada de comenzar a usar Arthas es a través de arthas-boot.jar. Este script de inicio descarga y ejecuta automáticamente la última versión de Arthas.
curl -O https://arthas.aliyun.com/arthas-boot.jar
java -jar arthas-boot.jar
Para ver todas las opciones disponibles con arthas-boot, puedes usar el flag -h:
java -jar arthas-boot.jar -h
Si experimentas problemas de velocidad al descargar el archivo debido a la ubicación geográfica, puedes utilizar el espejo de Aliyun:
java -jar arthas-boot.jar --repo-mirror aliyun --use-http
Instalación Completa
Para una instalación manual, descarga la última versión completa de Arthas desde su repositorio oficial o página de lanzamientos. Una vez descargado, descomprime el archivo y encontrarás el ejecutable arthas-boot.jar.
java -jar arthas-boot.jar
Es fundamental que el usuario que ejecuta Arthas tenga los mismos permisos que el proceso Java objetivo al que se intentará adjuntar. Por ejemplo, si tu aplicación se ejecuta bajo el usuario 'admin', deberías iniciar Arthas de la siguiente manera: sudo su admin && java -jar arthas-boot.jar o sudo -u admin -EH java -jar arthas-boot.jar. Si encuentras dificultades para adjuntar Arthas a un proceso, consulta los archivos de registro en ~/logs/arthas/ para obtener información detallada.
Desinstalación de Arthas
Para remover completamente Arthas de tu sistema, simplemente elimina los archivos de configuración y los logs generados:
En sistemas Linux/Unix/macOS
rm -rf ~/.arthas/
rm -rf ~/logs/arthas
En Windows
Elimina los directorios .arthas y logs/arthas que se encuentran en tu directorio de usuario (user home).
Uso Básico de Arthas
Iniciar Arthas y Conectarse a un Proceso Java
Una vez que ejecutes java -jar arthas-boot.jar, Arthas te presentará una lista de procesos Java en ejecución. Deberás seleccionar el número correspondiente al proceso al que deseas adjuntar Arthas. Por ejemplo, si tu aplicación de demostración es la segunda en la lista:
$ java -jar arthas-boot.jar
* [1]: 35542
[2]: 71560 arthas-demo.jar
Introduce 2 y presiona Enter. Arthas se conectará al proceso especificado y mostrará un mensaje de éxito junto con su interfaz de línea de comandos:
[INFO] Try to attach process 71560
[INFO] Attach process 71560 success.
[INFO] arthas-client connect 127.0.0.1 3658
,---. ,------. ,--------.,--. ,--. ,---. ,---.
/ O \ | .--. ''--. .--'| '--' | / O \ ' .-'
| .-. || '--'.' | | | .--. || .-. |`. `-.
| | | || |\ \ | | | | | || | | |.-' |
`--' `--'`--' '--' `--' `--' `--'`--' `--'`-----'
wiki: https://arthas.aliyun.com/doc
version: 3.0.5.20181127201536
pid: 71560
time: 2018-11-28 19:16:24
$
Visualizar el Dashboard del Sistema
El comando dashboard ofrece una vista en tiempo real y resumida del estado de la aplicación y del JVM. Incluye información sobre los hilos, uso de memoria, actividad del recolector de basura (GC) y detalles del entorno de ejecución. Para salir de la vista del dashboard, simplemente presiona Ctrl+C.
$ dashboard
ID NAME GROUP PRIORI STATE %CPU TIME INTERRU DAEMON
17 pool-2-thread-1 system 5 WAITIN 67 0:0 false false
27 Timer-for-arthas-dashb system 10 RUNNAB 32 0:0 false true
11 AsyncAppender-Worker-a system 9 WAITIN 0 0:0 false true
9 Attach Listener system 9 RUNNAB 0 0:0 false true
3 Finalizer system 8 WAITIN 0 0:0 false true
2 Reference Handler system 10 WAITIN 0 0:0 false true
4 Signal Dispatcher system 9 RUNNAB 0 0:0 false true
26 as-command-execute-dae system 10 TIMED_ 0 0:0 false true
13 job-timeout system 9 TIMED_ 0 0:0 false true
1 main main 5 TIMED_ 0 0:0 false false
14 nioEventLoopGroup-2-1 system 10 RUNNAB 0 0:0 false false
18 nioEventLoopGroup-2-2 system 10 RUNNAB 0 0:0 false false
23 nioEventLoopGroup-2-3 system 10 RUNNAB 0 0:0 false false
15 nioEventLoopGroup-3-1 system 10 RUNNAB 0 0:0 false false
Memory used total max usage GC
heap 32M 155M 1820M 1.77% gc.ps_scavenge.count 4
ps_eden_space 14M 65M 672M 2.21% gc.ps_scavenge.time(m 166
ps_survivor_space 4M 5M 5M s)
ps_old_gen 12M 85M 1365M 0.91% gc.ps_marksweep.count 0
nonheap 20M 23M -1 gc.ps_marksweep.time( 0
code_cache 3M 5M 240M 1.32% ms)
Runtime
os.name Mac OS X
os.version 10.13.4
java.version 1.8.0_162
java.home /Library/Java/JavaVir
tualMachines/jdk1.8.0
_162.jdk/Contents/Hom
e/jre
Identificar la Clase Principal con el Comando thread
Para determinar la clase principle (Main Class) de tu aplicación, puedes usar el comando thread. El hilo con ID 1 es comúnmente el hilo principal, que ejecuta el método main(). Puedes filtrar la salida para localizar rápidamente la clase:
$ thread 1 | grep 'main('
at demo.MathGame.main(MathGame.java:17)
Descompilar la Clase Principal con jad
Una vez que tienes el nombre de la clase principal, el comando jad te permite descompilar el bytecode de esa clase en código fuente Java legible. Esto es particularmente útil para comprender el funcionamiento de una aplicación sin tener acceso directo a su código fuente o para verificar la versión exacta de una clase cargada en el JVM.
$ jad demo.MathGame
La salida mostrará el código fuente descompilado:
ClassLoader:
+-sun.misc.Launcher$AppClassLoader@3d4eac69
+-sun.misc.Launcher$ExtClassLoader@66350f69
Location:
/tmp/arthas-demo.jar
/*
* Decompiled with CFR 0_132.
*/
package demo;
import java.io.PrintStream;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Random;
import java.util.concurrent.TimeUnit;
public class MathGame {
private static Random random = new Random();
private int illegalArgumentCount = 0;
public static void main(String[] args) throws InterruptedException {
MathGame game = new MathGame();
do {
game.run();
TimeUnit.SECONDS.sleep(1L);
} while (true);
}
public void run() throws InterruptedException {
try {
int number = random.nextInt();
List<Integer> primeFactors = this.primeFactors(number);
MathGame.print(number, primeFactors);
}
catch (Exception e) {
System.out.println(String.format("illegalArgumentCount:%3d, ", this.illegalArgumentCount) + e.getMessage());
}
}
public static void print(int number, List<Integer> primeFactors) {
StringBuffer sb = new StringBuffer("" + number + "=");
Iterator<Integer> iterator = primeFactors.iterator();
while (iterator.hasNext()) {
int factor = iterator.next();
sb.append(factor).append('*');
}
if (sb.charAt(sb.length() - 1) == '*') {
sb.deleteCharAt(sb.length() - 1);
}
System.out.println(sb);
}
public List<Integer> primeFactors(int number) {
if (number < 2) {
++this.illegalArgumentCount;
throw new IllegalArgumentException("number is: " + number + ", need >= 2");
}
ArrayList<Integer> result = new ArrayList<Integer>();
int i = 2;
while (i <= number) {
if (number % i == 0) {
result.add(i);
number /= i;
i = 2;
continue;
}
++i;
}
return result;
}
}
Affect(row-cnt:1) cost in 970 ms.
Observar el Valor de Retorno de Métodos con watch
El comando watch es una herramienta poderosa para observar la ejecución de métodos en tiempo real. Permite enspeccionar los parámetros, el valor de retorno o cualquier excepción lanzada por un método sin modificar el código fuente. A continuación, un ejemplo de cómo observar el valor de retorno del método primeFactors de la clase demo.MathGame:
$ watch demo.MathGame primeFactors returnObj
La salida mostrará las invocaciones del método y sus valores de retorno a lo largo del tiempo:
Press Ctrl+C to abort.
Affect(class-cnt:1 , method-cnt:1) cost in 107 ms.
ts=2018-11-28 19:22:30; [cost=1.715367ms] result=null
ts=2018-11-28 19:22:31; [cost=0.185203ms] result=null
ts=2018-11-28 19:22:32; [cost=19.012416ms] result=@ArrayList[
@Integer[5],
@Integer[47],
@Integer[2675531],
]
ts=2018-11-28 19:22:33; [cost=0.311395ms] result=@ArrayList[
@Integer[2],
@Integer[5],
@Integer[317],
@Integer[503],
@Integer[887],
]
ts=2018-11-28 19:22:34; [cost=10.136007ms] result=@ArrayList[
@Integer[2],
@Integer[2],
@Integer[3],
@Integer[3],
@Integer[31],
@Integer[717593],
]
ts=2018-11-28 19:22:35; [cost=29.969732ms] result=@ArrayList[
@Integer[5],
@Integer[29],
@Integer[7651739],
]
Salir de Arthas
Para cerrar únicamente la conexión de la consola actual sin detener la instancia de Arthas que está adjunta al proceso Java, utiliza los comandos quit o exit. La instancia de Arthas continuará ejecutándose en segundo plano en el proceso objetivo, manteniendo el puerto abierto para futuras reconexiones.
Si deseas detaner completamente la instancia de Arthas y desvincularla del proceso Java objetivo, emplea el comando stop.