El mes pasado, el código abierto Ali monitoreo y diagnóstico herramienta, " Arthas ", una herramienta en línea se puede utilizar para el análisis de problemas, las ganancias a corto plazo mucho la atención, incluso en Java oficial de Twitter Twitter remitieron, realmente elogios .
GitHub es el readme:
Arthas es una línea monitoreando productos de diagnóstico a través de una perspectiva global para ver la información de estado en tiempo real sobre la carga de aplicaciones, la memoria, GC, hilo, y puede, sin modificar el código de la aplicación, las cuestiones comerciales de diagnóstico, incluyendo acceso para ver la llamada al método ginseng, supervisión de excepciones método consume mucho tiempo, información de la carga de clases, en gran medida mejorar la eficiencia de la solución de problemas en línea.
Yo suelo ver las herramientas de código abierto interesantes, encontrará algunos sitios funcionales de corte mayor interés, desde la fuente de entender el principio de diseño e implementación. Para algunos de su propia comprensión de la realización de las ideas, y luego verificar el código fuente para ver si la idea es utilizar la misma aplicación. Si quieren lograr y lo mismo, se podría pensar, Aha, pensó un par de semanas. Si la fuente es otra implementación, se le quiere enfriar, lo que también puede jugar. Como si en el diálogo como autor y la fuente de la misma .
Aprovechando la celebración del Día Nacional y leer algunos " Arthas fuente", más o menos resume a continuación.
A partir de la estructura de paquetes fuente puede ser visto dividida en varios módulos principales:
-
Agente - VM cargado Agente de Aduanas
-
Cliente - aplicación cliente Telnet
-
Core - Arthas aplicación principal, incluye una conexión VM, resolver todo tipo de comandos, etc.
-
Sitio - Ayuda contenido del sitio libro Arthas
Miré las siguientes características principales:
-
El proceso de conexión
-
Descompilar clase, obtener el código fuente
-
Consulta carga clase especificada
El proceso de conexión
Conectar a un proceso específico, el seguimiento y diagnóstico de la fundación . Sólo primero asociar al proceso anterior, a fin de obtener información correspondiente a la máquina virtual, consulta ClassLoader cargado clases y así sucesivamente.
Cómo conectar con el proceso?
Los lectores para la herramienta de diagnóstico similar pueden tener la impresión, como JProfile, VisualVM y otras herramientas, le permitirá elegir un proceso de conectarse. Entonces operar en la máquina virtual especificada. Por ejemplo, para ver la información de la partición de memoria, la información recogida de basura de memoria correspondiente, realice BTrace guión y así sucesivamente.
Primero debemos pensar en ello, estos procesos una lista de conexiones disponibles, es la forma en que a la lista?
Por lo general, puede ser similar ps aux | grep java
herramienta para esto, o está usando Java proporcionado jps -lv
lista lata contiene el ID del proceso del contenido. Escribí en un artículo anterior, el contenido de la una en punto primeros JPS ( que puede no ser consciente de unos aparatos de Java ), por detrás de su aplicación, se todos los procesos locales de Java comenzará a pid
como nombre de archivo almacenado en el Java directorio temporal. Esta lista, que atraviesa estos archivos puede salir.
Arthas es cómo hacerlo?
En el script de arranque as.sh
, el código tiene en la lista de procesos a continuación, también alcanzado por jps
entonces excluir Jps fuera de su propia:
# check pid
if [ -z ${TARGET_PID} ] && [ ${BATCH_MODE} = false ]; then
local IFS_backup=$IFS
IFS=$'\n'
CANDIDATES=($(${JAVA_HOME}/bin/jps -l | grep -v sun.tools.jps.Jps | awk '{print $0}'))
if [ ${#CANDIDATES[@]} -eq 0 ]; then
echo "Error: no available java process to attach."
# recover IFS
IFS=$IFS_backup
return 1
fi
echo "Found existing java process, please choose one and hit RETURN."
index=0
suggest=1
# auto select tomcat/pandora-boot process
for process in "${CANDIDATES[@]}"; do
index=$(($index+1))
if [ $(echo ${process} | grep -c org.apache.catalina.startup.Bootstrap) -eq 1 ] \
|| [ $(echo ${process} | grep -c com.taobao.pandora.boot.loader.SarLauncher) -eq 1 ]
then
suggest=${index}
break
fi
done
Después de proceso de selección, que está conectado a un procedimiento especificado. En la porción de conexión attach
donde
# attach arthas to target jvm
# $1 : arthas_local_version
attach_jvm()
{
local arthas_version=$1
local arthas_lib_dir=${ARTHAS_LIB_DIR}/${arthas_version}/arthas
echo "Attaching to ${TARGET_PID} using version ${1}..."
if [ ${TARGET_IP} = ${DEFAULT_TARGET_IP} ]; then
${JAVA_HOME}/bin/java \
${ARTHAS_OPTS} ${BOOT_CLASSPATH} ${JVM_OPTS} \
-jar ${arthas_lib_dir}/arthas-core.jar \
-pid ${TARGET_PID} \
-target-ip ${TARGET_IP} \
-telnet-port ${TELNET_PORT} \
-http-port ${HTTP_PORT} \
-core "${arthas_lib_dir}/arthas-core.jar" \
-agent "${arthas_lib_dir}/arthas-agent.jar"
fi
}
La JVM adjuntar al interior alcanzado
por tools.jar
el paquete com.sun.tools.attach.VirtualMachine
y VirtualMachine.attach(pid)
de esta manera lograr.
Es a través de la parte inferior JVMTI
. Artículo anterior tenía un análisis simple JVMTI
técnica ( cuando hablamos de depuración, lo que estamos hablando (principio de aplicación de depuración) ), antes de ejecutar o de tiempo de ejecución, las cargas del agente y VM personalizados y llevar a cabo la comunicación .
En la implementación específica del contenido por encima de arthas-core.jar
la clase principal, nos fijamos en el contenido específico:
private void attachAgent(Configure configure) throws Exception {
VirtualMachineDescriptor virtualMachineDescriptor = null;
for (VirtualMachineDescriptor descriptor : VirtualMachine.list()) {
String pid = descriptor.id();
if (pid.equals(Integer.toString(configure.getJavaPid()))) {
virtualMachineDescriptor = descriptor;
}
}
VirtualMachine virtualMachine = null;
try {
if (null == virtualMachineDescriptor) { // 使用 attach(String pid) 这种方式
virtualMachine = VirtualMachine.attach("" + configure.getJavaPid());
} else {
virtualMachine = VirtualMachine.attach(virtualMachineDescriptor);
}
Properties targetSystemProperties = virtualMachine.getSystemProperties();
String targetJavaVersion = targetSystemProperties.getProperty("java.specification.version");
String currentJavaVersion = System.getProperty("java.specification.version");
if (targetJavaVersion != null && currentJavaVersion != null) {
if (!targetJavaVersion.equals(currentJavaVersion)) {
AnsiLog.warn("Current VM java version: {} do not match target VM java version: {}, attach may fail.",
currentJavaVersion, targetJavaVersion);
AnsiLog.warn("Target VM JAVA_HOME is {}, try to set the same JAVA_HOME.",
targetSystemProperties.getProperty("java.home"));
}
}
virtualMachine.loadAgent(configure.getArthasAgent(),
configure.getArthasCore() + ";" + configure.toString());
} finally {
if (null != virtualMachine) {
virtualMachine.detach();
}
}
}
Por VirtualMachine
, puede adjuntar a la corriente pid especificado, o bien a través de la VirtualMachineDescriptor
realización del proceso especificado adjuntar, el núcleo es esta frase:
virtualMachine.loadAgent(configure.getArthasAgent(),
configure.getArthasCore() + ";" + configure.toString());
Por lo tanto, VM y especificar el proceso de establecer una conexión, a continuación, puede comunicarse con amigos.
implementación de la clase Descompilar
Cuestionamos el diagnóstico, hay momentos en que necesita conocer el contenido de la clase correspondiente cargada para la clase cargada fácil identificación es correcta y así sucesivamente, por lo general javap
sólo muestra un resumen similar de los contenidos, no es intuitivo. En el lado de escritorio, podemos a través de jd-gui
este tipo de herramientas, la línea de comandos es generalmente mucho opcional.
Arthas integra esta funcionalidad.
Sustancialmente siguiente pasos:
-
Primero en encontrar el contenido de la clase especificada por el nombre de clase
-
Dependiendo de las opciones, determinar si se debe ver como clase interna
-
descompilar
Buscamos lograr los Arthas.
Para VM, especifique el nombre de la clase de mirada, vemos las siguientes líneas de código:
public void process(CommandProcess process) {
RowAffect affect = new RowAffect();
Instrumentation inst = process.session().getInstrumentation();
Set<Class<?>> matchedClasses = SearchUtils.searchClassOnly(inst, classPattern, isRegEx, code);
try {
if (matchedClasses == null || matchedClasses.isEmpty()) {
processNoMatch(process);
} else if (matchedClasses.size() > 1) {
processMatches(process, matchedClasses);
} else {
Set<Class<?>> withInnerClasses = SearchUtils.searchClassOnly(inst, classPattern + "(?!.*\\$\\$Lambda\\$).*", true, code);
processExactMatch(process, affect, inst, matchedClasses, withInnerClasses);
}
La clave es encontrar el contenido, hacer el paquete, SearchUtils
donde hay un núcleo del argumento: Instrumentation
son este tipo de lograr.
/**
* 根据类名匹配,搜已经被JVM加载的类
*
* @param inst inst
* @param classNameMatcher 类名匹配
* @return 匹配的类集合
*/
public static Set<Class<?>> searchClass(Instrumentation inst, Matcher<String> classNameMatcher, int limit) {
for (Class<?> clazz : inst.getAllLoadedClasses()) {
if (classNameMatcher.matching(clazz.getName())) {
matches.add(clazz);
}
}
return matches;
}
inst.getAllLoadedClasses()
, Son los grandes jugadores detrás.
Después de encontrar la clase, la forma de descompilarlo?
private String decompileWithCFR(String classPath, Class<?> clazz, String methodName) {
List<String> options = new ArrayList<String>();
options.add(classPath);
// options.add(clazz.getName());
if (methodName != null) {
options.add(methodName);
}
options.add(OUTPUTOPTION);
options.add(DecompilePath);
options.add(COMMENTS);
options.add("false");
String args[] = new String[options.size()];
options.toArray(args);
Main.main(args);
String outputFilePath = DecompilePath + File.separator + Type.getInternalName(clazz) + ".java";
File outputFile = new File(outputFilePath);
if (outputFile.exists()) {
try {
return FileUtils.readFileToString(outputFile, Charset.defaultCharset());
} catch (IOException e) {
logger.error(null, "error read decompile result in: " + outputFilePath, e);
}
}
return null;
}
Por tal método: decompileWithCFR
Así que hemos aprendido acerca de la descompilación es "de las herramientas de terceros CFR lograr." El código anterior se hace pasar después a la opción pelea principal método implementado CFR, y luego conservado. Amigos interesados pueden buscar benf cfr
para un uso específico.
Poner en práctica las clases de carga consulta
Después de leer el contenido de la anterior clase decompilados, sabemos que encapsula una SearchUtil
clase que será utilizado más adelante en muchos lugares, sino también en la consulta anterior descompilar continuación, después de la clase es. proceso de consulta, sino también sobre la base del instrumento, además de una variedad de filtro de regla de coincidencia, no se repetirán de manera más detalles.
Encontramos varias funciones para lograr lo anterior, hay dos cosas importantes:
-
Máquina virtual
-
Instrumentación
Arthas lógica entera se implementa en base Instrumentación de Java, todas las clases cargadas van cargando el agente a través después de addTransformer, mejorado, entonces el correspondiente Consejo de tejido en, busque el tipo de búsqueda, método, son se lleva a cabo por SearchUtil, toda la JVM carga partido de clase por su nombre por loadAllClass método de instrumentos, consistente serían devueltos.
La instrumentación es un buen compañero! :)
lectura relacionada
-
Una poderosa herramienta de monitorización de gestión Tomcat
-
Serie siete armas de Java anillo sentimental - versátil herramienta JVisual VM perfiles
-
Siete armas de Java serie longevidad espada - Java Virtual Machine microscopio de servicio Agente
Reloj " Tomcat esa cosa " , encontró más excelente artículo! Entender los principios detrás y respuestas a las preguntas más frecuentes. un análisis en profundidad de la fuente de los datos, contenido original, atención de bienvenida.