Aprendizaje práctico y análisis de la implementación remota en caliente de Java

Tabla de contenido

1. Análisis del estado y la necesidad de la implementación activa

(1) Definición de implementación en caliente y análisis de la situación actual

(2) Análisis de la dificultad de implementación técnica.

(3) Análisis de su necesidad

2. Ingrese a la práctica de implementación remota en caliente de Java de Meituan

(1) Análisis sónico

(2) Comparación de productos existentes en la industria.

(2) Experiencia práctica en la implementación y promoción de la implementación remota en caliente de Sonic.

3. Plan de diseño general

(1) estructura sónica

(2) Análisis de agentes

Análisis técnico del agente.

Descripción general de los principios básicos

Análisis de herramientas de instrumentos.

Análisis de instrumentación

Análisis del proceso de carga de Instrument Agent al inicio y en tiempo de ejecución.

(3) El “amor y muerte” entre JVM y HotSwap

Análisis del mecanismo HotSwap (mejoras JPDA)

Análisis JVMTI

Agentmain implementa análisis dinámico en tiempo de ejecución

(4) Sonic resuelve las limitaciones de la instrumentación.

Análisis dcevm

4. Análisis de la tecnología Sonic Hot Deployment

(1) Modelo de arquitectura general de Sonic

(2) Transferencia de función sónica

(3) Monitoreo de archivos

(4) Recarga de clase JVM

(5) Sobrecarga de Spring Bean

(6) Sobrecarga de Spring XML

(7) Implementación en caliente de MyBatis

5. Aplicación sónica

(1) Descripción general de las funciones de implementación en caliente

(2) Integración del complemento IDE


1. Análisis del estado y la necesidad de la implementación activa

(1) Definición de implementación en caliente y análisis de la situación actual

Java Hot Deployment se refiere a actualizar y recargar parte del código o recursos de una aplicación Java en tiempo de ejecución sin detener toda la aplicación. Esta capacidad es útil para la iteración rápida y la resolución de problemas durante el desarrollo y la depuración.

En el pasado, las capacidades de implementación en caliente de Java eran relativamente débiles y generalmente requerían el uso de marcos o herramientas específicos. Sin embargo, a medida que pasa el tiempo, la demanda de implementación en caliente entre los desarrolladores de Java continúa aumentando y han surgido muchas soluciones y herramientas nuevas para hacer que la implementación en caliente de Java sea más fácil y más común.

Estas son algunas de las tecnologías y herramientas de implementación de Java disponibles actualmente:

  1. JRebel : JRebel es una herramienta comercial que permite la implementación en caliente de código y recursos Java sin reiniciar la aplicación. Implementa capacidades de implementación en caliente mediante el uso de tecnología de cargador de clases y transformación de código de bytes. JRebel se usa ampliamente en entornos de desarrollo y procesos de depuración, pero requiere la compra de una licencia para usarlo.
  2. Spring Boot DevTools : Spring Boot DevTools es un conjunto de herramientas de desarrollo que proporciona muchas funciones convenientes durante el desarrollo, incluida la implementación en caliente. Puede monitorear los cambios de código y recursos durante el desarrollo y recargar automáticamente las partes correspondientes para acelerar el ciclo de desarrollo.
  3. DCEVM : Dynamic Code Evolution VM (DCEVM) es un parche de máquina virtual Java que amplía las capacidades de la JVM para hacer posible modificar las definiciones de clases en tiempo de ejecución sin reiniciar. DCEVM se utiliza junto con la tecnología HotSwap para lograr una implementación en caliente a nivel de clase.
  4. Integración de JRebel y DCEVM : JRebel y DCEVM se pueden utilizar juntos para lograr capacidades de implementación en caliente más potentes. Esta integración proporciona una cobertura de implementación en caliente más amplia y proporciona capacidades de depuración y desarrollo más avanzadas.

Aunque la capacidad de implementar Java en caliente ha mejorado en los últimos años, todavía existen algunas limitaciones y desafíos. Por ejemplo, ciertos tipos de cambios de código, como cambios en la estructura de herencia de una clase, pueden requerir que se reinicie la aplicación para que surtan efecto. Además, algunas soluciones de implementación en caliente pueden requerir entornos y arquitecturas de aplicaciones específicas.

En general, el estado actual de la implementación en caliente de Java ha mejorado significativamente, lo que permite a los desarrolladores desarrollar y depurar de manera más rápida y eficiente. Sin embargo, la elección específica depende de las necesidades de la aplicación y de las preferencias del equipo de desarrollo.

(2) Análisis de la dificultad de implementación técnica.

Las dificultades técnicas de la implementación en caliente de Java involucran principalmente los siguientes aspectos:

  1. Gestión de cargadores de clases y definiciones de clases : la implementación en caliente de Java se basa en el mecanismo de los cargadores de clases para cargar y recargar definiciones de clases. Durante el proceso de implementación en caliente, es necesario administrar correctamente la relación entre los cargadores de clases y las clases para garantizar que las nuevas definiciones de clases se puedan cargar correctamente y reemplazar las definiciones antiguas, evitando al mismo tiempo problemas como pérdidas de memoria y conflictos de carga de clases.
  2. Transformación de código de bytes y modificación dinámica : durante la implementación en caliente, el código de bytes debe transformarse y modificarse para aplicar código y recursos nuevos a la aplicación en ejecución. Esto implica analizar, modificar y recargar el código de bytes, al tiempo que se garantiza la corrección y coherencia del código de bytes modificado para evitar comportamientos anormales o fallas de la aplicación.
  3. Gestión y actualización de recursos : además de las definiciones de clases, la implementación en caliente también debe considerar la actualización y gestión de otros recursos en la aplicación, como archivos de configuración, archivos de plantilla, etc. Garantizar que estos recursos se carguen y apliquen correctamente y se actualicen en sincronía con el código es un desafío.
  4. Coherencia del estado y de los datos : la implementación en caliente puede alterar la lógica empresarial y el procesamiento de datos en curso. Al recargar código y recursos, debe considerar cómo mantener el estado de la aplicación y la coherencia de los datos para evitar la pérdida de datos, errores comerciales o inconsistencias.
  5. Adaptación a marcos y entornos específicos : diferentes marcos Java y servidores de aplicaciones pueden admitir la implementación en caliente en distintos grados y de diferentes maneras. Al implementar la implementación en caliente, es necesario adaptar y ajustar el marco y el entorno específicos para garantizar la estabilidad y compatibilidad de la función de implementación en caliente.

Estas dificultades técnicas deben considerarse de manera integral, resolverse e implementarse en marcos y herramientas de implementación en caliente. Diferentes herramientas y marcos pueden adoptar diferentes métodos y estrategias para abordar estos desafíos y proporcionar capacidades de implementación en caliente confiables y eficientes.

En la actualidad, la serie Java presentada oficialmente por Meituan | La práctica de implementación de implementación remota en caliente en Meituan: nació el complemento de implementación en caliente Sonic del equipo técnico de Meituan.

(3) Análisis de su necesidad

En términos generales, durante el proceso de desarrollo de la mayoría de las empresas, los ingenieros necesitan lanzar servicios varias veces al día mientras prueban y depuran los requisitos correspondientes. La mayoría de las aplicaciones tardan entre 3 y 10 minutos cada vez. Además, el tiempo único para construir, implementar y El procesamiento de imágenes se concentra en 20 minutos (aproximadamente 45 minutos). La implementación es frecuente y requiere mucho tiempo, lo que afecta seriamente la eficiencia de la implementación del sistema. La implementación en caliente es una necesidad importante en el desarrollo de Java, aquí hay un análisis:

  1. Mejore la eficiencia del desarrollo: la implementación en caliente permite a los desarrolladores actualizar el código y los recursos sin reiniciar toda la aplicación. Esto significa que los desarrolladores pueden realizar modificaciones y experimentos de código más rápido, iterando y depurando aplicaciones rápidamente. La implementación en caliente puede reducir el ciclo de desarrollo y mejorar la eficiencia del desarrollo.
  2. Permite retroalimentación y depuración rápidas: la implementación en caliente permite a los desarrolladores ver inmediatamente los resultados de los cambios que realizan en su código, sin la necesidad de reconstruir y reiniciar la aplicación. Este mecanismo de retroalimentación rápida puede ayudar a los desarrolladores a depurar y solucionar problemas rápidamente, mejorando la eficiencia y la calidad del desarrollo.
  3. Reduzca el tiempo de inactividad del sistema: los métodos de implementación tradicionales a menudo requieren detener la aplicación, volver a implementarla e iniciarla, lo que resulta en un tiempo de inactividad del sistema. La implementación en caliente evita este tiempo de inactividad, lo que permite que partes del código y los recursos se actualicen y recarguen en tiempo de ejecución, lo que permite actualizaciones y mejoras de aplicaciones sin problemas.
  4. Experiencia de usuario mejorada: la implementación en caliente permite actualizaciones de aplicaciones sin interrumpir los procesos comerciales en curso. Esto significa que los usuarios pueden obtener las funciones y correcciones más recientes mientras usan la aplicación, mejorando la experiencia y la satisfacción del usuario.
  5. Admite configuración dinámica y flexibilidad: la implementación en caliente permite la modificación dinámica de la configuración y el comportamiento de una aplicación sin la necesidad de reiniciar la aplicación. Esto proporciona mayor flexibilidad y capacidad de configuración, permitiendo ajustes y configuración en tiempo real según sea necesario.

Aunque la implementación en caliente tiene muchas ventajas y necesidades en el desarrollo de Java, también existen algunos desafíos y limitaciones potenciales que se deben tener en cuenta. Por ejemplo, algunos tipos de cambios de código pueden requerir que se reinicie la aplicación para que surtan efecto, y la implementación en caliente puede introducir cierta complejidad en el tiempo de ejecución y requerir herramientas y soporte de marco adecuados. Por lo tanto, al seleccionar y utilizar tecnología de implementación en caliente, se deben considerar de manera integral factores como los requisitos del proyecto, el nivel técnico del equipo de desarrollo y los costos de las herramientas.

Si se cumplen las condiciones de implementación en caliente, ayudará a los programadores a desempeñar un papel importante en el desarrollo y la depuración conjunta de autoprueba. El siguiente es un cuadro comparativo después de que Meituan se conectó y usó Sonic.

Desarrollar escenarios de autoevaluación

Escena de depuración conjunta

2. Ingrese a la práctica de implementación remota en caliente de Java de Meituan

(1) Análisis sónico

Sonic es un complemento de IDEA desarrollado y diseñado internamente por Meituan, cuyo objetivo es ayudar a la implementación remota/local en caliente a través del desarrollo de código bajo, resolver problemas de eficiencia en la codificación, escritura y ejecución de pruebas únicas, autopruebas y depuración conjunta, etc. y mejorar la productividad de codificación de los desarrolladores. Eficiencia de salida. Las estadísticas muestran que los desarrolladores dedican aproximadamente el 35% de su tiempo diario a la codificación. Si desea mejorar la eficiencia de la I + D, debe ampliar la proporción de tiempo de la salida de codificación o mejorar la eficiencia de salida de la etapa de codificación. Sonic se enfoca en mejorar la eficiencia de salida de la etapa de codificación.

Actualmente, el uso de la implementación en caliente de Sonic puede resolver la mayoría de los problemas de creación de duplicación de código. Sonic permite a los usuarios escribir código localmente e implementarlo en un entorno remoto con un solo clic, modificar el código, implementar, depurar solicitudes de forma conjunta, ver registros y repetir el ciclo. Si no se tiene en cuenta el tiempo de modificación del código, un ciclo suele tardar entre 20 y 35 minutos. El uso de Sonic puede acortar el tiempo total de 5 a 10 segundos y puede proporcionar a los desarrolladores una experiencia de desarrollo eficiente e inmersiva. En el trabajo de codificación real, las modificaciones de múltiples archivos son comunes, y las capacidades de implementación en caliente de Sonic para múltiples archivos son particularmente sobresalientes. Puede realizar la implementación remota en caliente de múltiples archivos en lotes a través del análisis de dependencia y otros medios, y admite Spring Bean Class, Ordinary Class, Spring Mixed implementa en caliente XML, MyBatis XML y otros tipos de archivos.

(2) Comparación de productos existentes en la industria.

JRebel admite muchos complementos de terceros y tiene un ecosistema enorme, pero no admite complementos nacionales, como FastJson, etc. Al mismo tiempo, también tiene limitaciones en la configuración de implementación remota en caliente. La empresa requiere un desarrollo personalizado y es un software comercial, el costo de uso es mayor .

(2) Experiencia práctica en la implementación y promoción de la implementación remota en caliente de Sonic.

Para la promoción de productos técnicos, especialmente productos utilizados en las etapas de desarrollo y prueba, dado que están lejos del entorno en línea, si la fuerza motriz, el poder de ejecución y la función del producto se pueden realizar bien en un circuito cerrado determinará si el producto puede lanzarse dentro de la empresa y ganar una gran popularidad. Una parte importante reconocida por la mayoría de la gente. Además, debido a que muchos desarrolladores han formado gradualmente "acciones solidificadas" durante las etapas de desarrollo y prueba, cómo cambiar el comportamiento de estos usuarios y permitirles adoptar nuevos productos es también uno de los desafíos difíciles que enfrenta Sonic.

El equipo de Sonic ha implementado los cuatro principios anteriores a partir de la comunicación activa, el acceso rápido sin costo (o de costo extremadamente bajo), los scripts automatizados, el diagnóstico automático del producto y la recopilación de comentarios.

3. Plan de diseño general

(1) estructura sónica

El complemento Sonic consta de 4 partes, que incluyen el lado del script, el lado del complemento, el lado del agente y el lado del servidor Sonic. El lado del script es responsable de crear automáticamente los parámetros de inicio de Sonic, el inicio del servicio y otros trabajos de integración; el entorno de integración del lado del complemento IDEA proporciona a los desarrolladores servicios de implementación en caliente más convenientes; el lado del Agente es responsable de implementar la función de implementación en caliente cuando el proyecto Después de iniciar, el lado del servidor es responsable de recopilar información de implementación en caliente, informes de fallas y otros trabajos estadísticos. Como se muestra abajo:

(2) Análisis de agentes

Análisis técnico del agente.

Agente en el desarrollo de software se refiere a una herramienta o biblioteca que puede monitorear, modificar y ampliar una aplicación mientras se está ejecutando. El agente puede implementar la implantación y operación de aplicaciones mediante inyección de código de bytes, proxy dinámico, conversión de código y otras tecnologías .

La tecnología de agentes juega un papel importante y se utiliza ampliamente en el desarrollo de Java. El siguiente es un análisis de la tecnología de agentes:

  1. Monitoreo y diagnóstico dinámicos : el agente puede monitorear y recopilar varios indicadores e información mientras la aplicación se está ejecutando, como indicadores de rendimiento, uso de memoria, llamadas a métodos, etc. Esto es útil para el análisis del rendimiento de las aplicaciones, el diagnóstico de problemas y la optimización. El agente puede capturar eventos clave y generar registros o informes correspondientes a través de lógica y reglas personalizadas.
  2. Mejora y corrección de código : el agente puede implementar mejoras y correcciones de código en el programa de aplicación modificando el código de bytes en tiempo de ejecución. Por ejemplo, puede insertar lógica personalizada antes y después de las llamadas a métodos para modificar o verificar parámetros. Esta capacidad permite personalizar y mejorar las aplicaciones sin modificar el código fuente.
  3. Carga y descarga dinámica : el agente puede cargar y descargar definiciones de clases dinámicamente a través de un mecanismo de carga de clases personalizado. Esto es útil para implementar arquitectura de complementos, desarrollo modular y expansión dinámica. El agente puede cargar dinámicamente nuevas clases y recursos según sea necesario y descargarlos cuando no sea necesario, logrando así dinamismo y flexibilidad de la aplicación .
  4. Verificación de seguridad y control de permisos : el agente puede realizar una verificación de seguridad y control de permisos en el código mientras la aplicación se está ejecutando. Por ejemplo, puede interceptar y autenticar llamadas a métodos confidenciales o realizar comprobaciones de permisos de acceso a recursos restringidos. Esto ayuda a mejorar la seguridad y la controlabilidad de las aplicaciones.
  5. Compatibilidad con AOP (programación orientada a aspectos) : el agente se puede utilizar junto con el marco AOP para gestionar y operar las preocupaciones transversales de la aplicación. A través del Agente, las aplicaciones se pueden programar en tiempo de ejecución para implementar funciones comunes como registro, monitoreo del rendimiento y gestión de transacciones.

Cabe señalar que la tecnología del agente debe usarse con precaución porque la implantación en tiempo de ejecución y la manipulación de aplicaciones pueden generar complejidad y sobrecarga de rendimiento. Al mismo tiempo, para algunos entornos sensibles a la seguridad, el uso del Agente y el control de permisos deben evaluarse cuidadosamente para garantizar la seguridad y estabilidad del sistema.

Algunas herramientas del Agente Java conocidas incluyen Byte Buddy, AspectJ, API del Agente Java, etc. Estas herramientas proporcionan funciones ricas y extensibilidad flexible, lo que permite a los desarrolladores personalizar y utilizar la tecnología del agente según sea necesario.

Descripción general de los principios básicos

Los principios básicos del Agente implican operaciones de código de bytes y mecanismos de carga de clases. El siguiente es un análisis de los principios básicos del Agente:

  1. Operación de código de bytes : el agente opera el código de bytes de la aplicación para mejorar y corregir la aplicación. Las operaciones de código de bytes pueden incluir insertar nuevas instrucciones, modificar instrucciones existentes, eliminar instrucciones, etc. Este tipo de operación puede cambiar dinámicamente el comportamiento de la aplicación en tiempo de ejecución.
  2. Mecanismo de carga de clases : el agente utiliza el mecanismo de carga de clases de Java para cargar y definir clases de agente. Carga la clase de agente generada por el Agente a través de un cargador de clases personalizado y la define como una nueva definición de clase. Esto permite al Agente crear y cargar clases dinámicamente en tiempo de ejecución sin modificar la definición de clase original.
  3. Proxy dinámico : el agente a menudo utiliza tecnología de proxy dinámico para implementar la implantación y operación de aplicaciones. Los proxies dinámicos pueden interceptar llamadas a métodos de la clase original creando clases de proxy y objetos de proxy, e insertando lógica personalizada antes y después de las llamadas. Esto permite realizar mejoras y correcciones en la aplicación sin modificar las clases originales.
  4. Mecanismo de conexión JVM : el agente generalmente utiliza el mecanismo de conexión JVM para conectarse al proceso Java de destino. El mecanismo JVM Attach permite al Agente acceder al interior del proceso de destino y comunicarse con la máquina virtual Java. Esto permite al Agente inyectar su propio código en el proceso de destino en tiempo de ejecución e interactuar con la aplicación.
  5. API del Agente Java: Java proporciona la API del Agente Java, que define un conjunto de interfaces y clases para escribir y administrar agentes Java. A través de la API del Agente Java, el Agente puede registrar métodos de devolución de llamada y realizar el procesamiento correspondiente cuando ocurren eventos como carga de aplicaciones, inicialización, conversión, etc.

El principio básico del Agente es lograr una mejora y corrección dinámica de las aplicaciones mediante la operación de códigos de bytes y cargadores de clases en tiempo de ejecución. Permite a los desarrolladores interceptar y modificar el comportamiento de las aplicaciones a través de lógica y reglas personalizadas, personalizando y extendiendo así las aplicaciones. Esta capacidad juega un papel importante en el monitoreo, diagnóstico, control de seguridad, AOP y otros escenarios.

Análisis de herramientas de instrumentos.

Instrument es una clase de herramienta en el lenguaje Java, ubicada en el paquete java.lang.instrument. Proporciona un conjunto de herramientas y métodos para convertir el código de bytes de aplicaciones Java en tiempo de ejecución. La API de instrumentación permite a los desarrolladores modificar el código de bytes, definir nuevas clases y redefinir las clases cargadas durante el proceso de carga de clases.

El instrumento se utiliza principalmente para desarrollar el Agente Java, que proporciona al Agente capacidades operativas y de acceso subyacentes a las aplicaciones Java. A través de la API de instrumentación, los desarrolladores pueden realizar transformaciones y operaciones de código de bytes personalizadas durante el ciclo de vida de una aplicación Java. Las principales funciones del Instrumento incluyen:

  1. Registro de un convertidor (Transformer) : el instrumento proporciona el método addTransformer para registrar un convertidor de clases personalizado (ClassFileTransformer). Un convertidor de clases es una clase que implementa la interfaz ClassFileTransformer y se utiliza para convertir el código de bytes de una clase durante el proceso de carga de clases.
  2. Transformar el código de bytes de una clase : el método de transformación de la interfaz ClassFileTransformer permite a los desarrolladores modificar el código de bytes de una clase. Al implementar este método, puede realizar operaciones personalizadas en el código de bytes de la clase cuando se carga la clase, como insertar nuevas instrucciones, modificar instrucciones existentes, etc.
  3. Redefinir clases : el instrumento proporciona el método redefineClasses, que permite a los desarrolladores redefinir las clases cargadas. La redefinición y el reemplazo de clases se pueden lograr reemplazando la definición de una clase cargada proporcionando un nuevo código de bytes de clase.
  4. Obtener la definición de una clase : el método getAllLoadedClasses de Instrument puede obtener la definición de todas las clases actualmente cargadas. Al recorrer las clases cargadas, los desarrolladores pueden obtener el nombre de la clase, el código de bytes y otra información.
  5. Agregue clases de proxy dinámicamente : el método appendToBootstrapClassLoaderSearch del instrumento puede agregar clases de proxy a la ruta de búsqueda del cargador de clases de inicio. De esta manera, la clase proxy puede ser cargada por el cargador de clases de arranque y así funcionar en toda la aplicación.

El uso de Instrumento generalmente se implementa en combinación con el Agente Java. El Agente Java utiliza la API de Instrumentación para implantar y operar dinámicamente aplicaciones Java. A través de una instancia de Instrumentación, el Agente puede registrar un convertidor de clases y convertir y redefinir clases, personalizando y mejorando así la aplicación.

Análisis de instrumentación

La instrumentación es una API proporcionada por Java y se encuentra en el paquete java.lang.instrument. Proporciona un conjunto de herramientas y métodos para convertir códigos de bytes mientras se ejecutan aplicaciones Java. La API de instrumentación permite a los desarrolladores modificar el código de bytes, definir nuevas clases y redefinir las clases cargadas durante el proceso de carga de clases.

El agente utiliza la API de instrumentación para implementar la implantación dinámica y el funcionamiento de las aplicaciones. Mediante la combinación de API de agente e instrumentación, las clases se pueden convertir, modificar y mejorar durante la fase de carga de la aplicación o el tiempo de ejecución. Específicamente, el Agente puede registrar un ClassFileTransformer a través de la API de Instrumentación e implementar su método de transformación para convertir el código de bytes de la clase.

Cuando se inicia la aplicación Java, el Agente se adjunta al proceso de destino a través del mecanismo de conexión JVM y obtiene la instancia de Instrumentación en el método premain o método agentmain. Luego, el Agente usa la instancia de Instrumentación para registrar un ClassFileTransformer personalizado y transformar la clase durante el proceso de carga de clases.

La API de instrumentación proporciona las siguientes funciones para respaldar la implementación del Agente:

  1. Registrar ClassFileTransformer : el agente utiliza el método addTransformer de la instancia de Instrumentación para registrar un ClassFileTransformer personalizado para transformar clases durante el proceso de carga de clases.
  2. Transformar el código de bytes de la clase : el método de transformación de la interfaz ClassFileTransformer permite al Agente modificar y transformar el código de bytes de la clase. El agente puede obtener el código de bytes de la clase en este método y realizar operaciones personalizadas en él, como insertar nuevas instrucciones, modificar instrucciones existentes, etc.
  3. Redefinir clases : el método redefineClasses de la API de Instrumentación permite al Agente redefinir las clases cargadas. El agente puede redefinir y reemplazar clases proporcionando un nuevo código de bytes de clase para reemplazar la definición de clases cargadas.

La combinación de Instrumentación y Agente permite a los desarrolladores implantar y operar dinámicamente aplicaciones Java en tiempo de ejecución para personalizar, mejorar y modificar aplicaciones. A través de la API de Instrumentación, los Agentes pueden modificar el comportamiento de las aplicaciones de forma no intrusiva sin modificar el código fuente.

Las API de uso común de la clase Instrumentación son las siguientes:

public interface Instrumentation {

    //增加一个Class 文件的转换器,转换器用于改变 Class 二进制流的数据,参数 canRetransform 设置是否允许重新转换。
    void addTransformer(ClassFileTransformer transformer, boolean canRetransform);

    //在类加载之前,重新定义 Class 文件,ClassDefinition 表示对一个类新的定义,
    //如果在类加载之后,需要使用 retransformClasses 方法重新定义。addTransformer方法配置之后,后续的类加载都会被Transformer拦截。
    //对于已经加载过的类,可以执行retransformClasses来重新触发这个Transformer的拦截。类加载的字节码被修改后,除非再次被retransform,否则不会恢复。
    void addTransformer(ClassFileTransformer transformer);

    //删除一个类转换器
    boolean removeTransformer(ClassFileTransformer transformer);
    
    //是否允许对class retransform
    boolean isRetransformClassesSupported();

    //在类加载之后,重新定义 Class。这个很重要,该方法是1.6 之后加入的,事实上,该方法是 update 了一个类。
    void retransformClasses(Class<?>... classes) throws UnmodifiableClassException;
   
    //是否允许对class重新定义
    boolean isRedefineClassesSupported();

    //此方法用于替换类的定义,而不引用现有的类文件字节,就像从源代码重新编译以进行修复和继续调试时所做的那样。
    //在要转换现有类文件字节的地方(例如在字节码插装中),应该使用retransformClasses。
    //该方法可以修改方法体、常量池和属性值,但不能新增、删除、重命名属性或方法,也不能修改方法的签名
    void redefineClasses(ClassDefinition... definitions) throws  ClassNotFoundException, UnmodifiableClassException;

    //获取已经被JVM加载的class,有className可能重复(可能存在多个classloader)
    @SuppressWarnings("rawtypes")
    Class[] getAllLoadedClasses();
}

Análisis del proceso de carga de Instrument Agent al inicio y en tiempo de ejecución.

(3) El “amor y muerte” entre JVM y HotSwap

Se realizan mejoras constantemente en la JVM HotSwap que rodea al cuerpo del método.

A partir de la versión 1.4, JPDA introduce el mecanismo HotSwap (mejoras de JPDA) para realizar la dinámica del cuerpo del método durante la depuración. Puede consultar el documento: mejoras1.4  .
A partir de la versión 1.5, el modo Premain de java.lang.instrument (Java Platform SE 8) implementado por JVMTI se utiliza para realizar la dinámica del modo Agente (el Agente se especifica cuando se inicia la JVM) . Puede consultar el documento: resumen del paquete .

La versión 1.6 agrega el modo Agentmain para lograr dinámica de tiempo de ejecución (vinculación a una VM específica a través de The Attach API) . Puede consultar el documento: resumen del paquete  . La implementación básica es actualizar el método y el código de bytes a nivel de cuerpo a través de retransformClass/redefineClass de JVMTI. ASM y CGLib básicamente crean dinámicas en torno a estos. Sin embargo, no se ha realizado ninguna acción para HotSwap for Class (como agregar métodos, agregar campos, modificar relaciones de herencia, etc.) a Class. ¿Por qué sucede esto? Porque la complejidad es demasiado alta y no hay un alto rendimiento.

Análisis del mecanismo HotSwap (mejoras JPDA)

El mecanismo HotSwap es una mejora funcional proporcionada por Java Platform Debugging Architecture (JPDA). JPDA es parte de la plataforma Java y se utiliza para ayudar a los desarrolladores a realizar operaciones de depuración interactivas al depurar aplicaciones.

El mecanismo HotSwap permite modificaciones de las clases cargadas mientras la aplicación se está ejecutando sin reiniciarla. Esto es útil para una iteración y depuración rápidas durante el desarrollo, ya que los desarrolladores pueden realizar cambios en el código en tiempo de ejecución y ver inmediatamente los efectos de las modificaciones sin tener que detener y reiniciar la aplicación.

Las principales características y principios del mecanismo HotSwap incluyen los siguientes aspectos:

  1. Actualizar dinámicamente el código de bytes : el mecanismo HotSwap implementa actualizaciones de clases reemplazando el código de bytes de la clase en tiempo de ejecución. Los desarrolladores pueden utilizar un depurador u otra herramienta habilitada para JPDA para editar el código de bytes de una clase y enviar el código de bytes modificado a la máquina virtual en ejecución.
  2. Reemplazo en caliente del código : el mecanismo HotSwap admite la modificación del cuerpo del método para lograr el reemplazo del código en caliente. Los desarrolladores pueden cambiar el código de implementación de un método en tiempo de ejecución y luego recargar el método para que el nuevo código entre en vigor de inmediato.
  3. Limitaciones y restricciones : el mecanismo HotSwap tiene algunas limitaciones y restricciones. Por ejemplo, solo puede reemplazar el cuerpo del método, pero no agregar nuevos campos o métodos. Además, los cambios de código en algunos escenarios pueden requerir la reinicialización o la reconexión de la aplicación.
  4. Compatibilidad con JPDA : el mecanismo HotSwap se proporciona a través de Java Platform Debug Architecture (JPDA). JPDA es un conjunto de API y protocolos para depurar y monitorear aplicaciones Java. A través de las funciones mejoradas de JPDA, los desarrolladores pueden utilizar el mecanismo HotSwap para satisfacer la necesidad de actualizar el código en tiempo de ejecución.

En resumen, el mecanismo HotSwap es una mejora funcional de JPDA que permite a los desarrolladores modificar las clases cargadas en tiempo de ejecución para lograr un reemplazo en caliente del código. Proporciona a los desarrolladores una forma de iterar y depurar código rápidamente, ahorrando tiempo al reiniciar aplicaciones y mejorando la eficiencia del desarrollo.

Análisis JVMTI

JVMTI (Java Virtual Machine Tool Interface) es una interfaz de máquina virtual Java que permite a los desarrolladores desarrollar y conectar herramientas a la máquina virtual Java para monitorear, depurar y analizar la ejecución de aplicaciones Java.

El siguiente es un análisis de JVMTI:

  1. Descripción general funcional: JVMTI proporciona un conjunto de API que permiten a los desarrolladores escribir herramientas para monitorear y controlar el comportamiento de la máquina virtual Java. Permite a los desarrolladores obtener información sobre clases, objetos, subprocesos, pilas, memoria, etc. durante la ejecución de la aplicación, y modificarla y controlarla dinámicamente.
  2. Funciones de monitoreo y depuración: JVMTI permite a los desarrolladores monitorear eventos que ocurren en la máquina virtual Java a través del mecanismo de notificación de eventos, como carga de clases, invocación de métodos, lanzamiento de excepciones, etc. Los desarrolladores pueden registrar detectores de eventos para realizar las acciones apropiadas cuando ocurren eventos. Además, JVMTI también brinda la capacidad de monitorear y analizar subprocesos, pilas, memoria, etc.
  3. Función de modificación dinámica: JVMTI permite a los desarrolladores modificar las clases de Java mientras la aplicación se está ejecutando. Los desarrolladores pueden utilizar las funciones proporcionadas por JVMTI para redefinir clases, modificar cuerpos de métodos, agregar campos, etc. Esta capacidad de modificar dinámicamente es muy útil para algunos escenarios de aplicaciones específicos (como el reemplazo en caliente de código).
  4. Funciones de análisis de rendimiento: JVMTI también proporciona algunas herramientas de análisis de rendimiento, como análisis de memoria, análisis de subprocesos y análisis de CPU. Los desarrolladores pueden utilizar estas herramientas para identificar cuellos de botella en el rendimiento, pérdidas de memoria y otros problemas y optimizar el rendimiento de las aplicaciones Java.
  5. Conexiones integradas y remotas: JVMTI admite la integración de agentes de herramientas dentro de la máquina virtual Java y también admite conexiones remotas para interactuar con aplicaciones que se ejecutan en máquinas virtuales Java remotas. Esto permite a los desarrolladores monitorear y depurar de forma remota aplicaciones Java distribuidas.

En resumen, JVMTI es la interfaz de herramientas de máquina virtual Java, que proporciona un conjunto de API y funciones que permiten a los desarrolladores desarrollar y conectar herramientas a la máquina virtual Java. Proporciona la capacidad de monitorear, depurar, analizar y modificar dinámicamente aplicaciones Java, brindando a los desarrolladores herramientas e interfaces ricas para comprender y controlar en profundidad la ejecución de las aplicaciones Java.

Agentmain implementa análisis dinámico en tiempo de ejecución

Agentmain es un método en el mecanismo del Agente Java que se utiliza para cargar e inicializar dinámicamente el Agente Java cuando la aplicación se está ejecutando. A través de Agentmain, los desarrolladores pueden cargar el Agente Java en la máquina virtual Java en ejecución después de iniciar la aplicación y realizar un análisis dinámico de la aplicación en tiempo de ejecución.

Los principios y pasos de implementación de Agentmain son los siguientes:

  1. Escribir el Agente Java: Primero, los desarrolladores deben escribir un Agente Java, que contiene las funciones y la lógica necesarias para la implementación. El Agente Java puede utilizar la API de Instrumentación para monitorear y modificar aplicaciones en ejecución.
  2. Inicie la aplicación: luego, el desarrollador debe iniciar la aplicación de destino, ya sea a través de la línea de comando u otros medios. Al iniciar la aplicación, debe especificar la opción -javaagent en los parámetros de inicio de JVM y pasar la ruta del archivo JAR del Agente Java como parámetro al parámetro -agentmain.
  3. Cargar Agentmain: después de que se inicia la aplicación, la máquina virtual Java carga e inicializa automáticamente Agentmain. Se llamará al método premain en Agentmain para inicializar el Agente Java. En el método premain, los desarrolladores pueden obtener la instancia de Instrumentación y registrar los monitores y convertidores necesarios.
  4. Análisis dinámico en tiempo de ejecución: una vez que Agentmain se carga e inicializa correctamente, los desarrolladores pueden usar la API de instrumentación para realizar un análisis dinámico de la aplicación en ejecución. Por ejemplo, puede agregar interceptores de métodos, recopilar métricas de rendimiento, registrar, etc. A través de la API de instrumentación, puede modificar la definición de clase, reemplazar el cuerpo del método o agregar lógica de código de bytes adicional.

En resumen, Agentmain es un método de implementación en el mecanismo del Agente Java, que se utiliza para cargar e inicializar dinámicamente el Agente Java cuando la aplicación se está ejecutando. A través de Agentmain, los desarrolladores pueden utilizar la API de instrumentación para analizar y modificar dinámicamente las aplicaciones en ejecución. Esto proporciona a los desarrolladores una herramienta flexible y poderosa para implementar una variedad de capacidades de monitoreo, análisis y optimización dentro del entorno de ejecución de la aplicación.

(4) Sonic resuelve las limitaciones de la instrumentación.

Debido a las limitaciones de JVM, tanto JDK 7 como JDK 8 no permiten cambios en la estructura de clases, como agregar campos, agregar métodos y modificar la clase principal de la clase, etc. Esto es fatal para los proyectos Spring. Por ejemplo, un desarrollador quiere modificar un Spring Bean y agregar un campo @Autowired. Existen muchos escenarios de este tipo en aplicaciones prácticas, por lo que el soporte de Sonic para tales escenarios es esencial.

Entonces, ¿cómo se hace exactamente? Aquí me gustaría mencionar el "famoso" Dcevm. Dcevm (DynamicCode Evolution Virtual Machine) es un parche (estrictamente hablando, una modificación) de Java Hostspot que permite (pero no ilimitadamente) la modificación de archivos de clase cargados en el entorno de ejecución. La máquina virtual actual solo permite modificar el cuerpo del método (Method, Body), mientras que Decvm puede agregar, eliminar atributos de clase, métodos e incluso cambiar la clase padre de una clase. Dcevm es un proyecto de código abierto que cumple con la GPL 2.0. acuerdo. Para obtener más información sobre Dcevm, puede consultar: Wuerthinger10a y GitHub Decvm .

Vale la pena mencionar que en Meituan, Sonic ha abierto HULK para la instalación de Dcevm, que se puede completar integrando la imagen de lanzamiento (la implementación local en caliente se puede combinar con la función de complemento para lograr un entorno de implementación en caliente de instalación con un solo clic). ).

Análisis dcevm

DCEVM (Dynamic Code Evolution VM) es una herramienta mejorada para Java Virtual Machine (JVM) que permite modificar y recargar archivos de clase Java en tiempo de ejecución, lo que permite una implementación en caliente y capacidades de desarrollo rápido.

El siguiente es un análisis de DCEVM:

  1. Implementación en caliente y desarrollo rápido : el objetivo principal de DCEVM es proporcionar una forma conveniente para la implementación en caliente y el desarrollo rápido. En el proceso de desarrollo tradicional de Java, la modificación de archivos de clases de Java generalmente requiere volver a compilar y reiniciar la aplicación. DCEVM permite cargar y reemplazar dinámicamente archivos de clases Java modificados sin reiniciar la aplicación, logrando un efecto inmediato.
  2. Mecanismo de carga de clases mejorado : DCEVM mejora el mecanismo de carga de clases de la JVM para permitirle cargar y descargar dinámicamente archivos de clases en tiempo de ejecución. Proporciona una implementación de cargador de clases más flexible que la JVM estándar, capaz de cargar nuevas definiciones de clases y reemplazar las definiciones de clases cargadas mientras la aplicación se está ejecutando.
  3. Reemplazo rápido de código en caliente : DCEVM también proporciona la función de reemplazo rápido de código en caliente (HotSwap). Permite a los desarrolladores modificar el cuerpo del método de una clase cargada mientras la aplicación se está ejecutando, con efecto inmediato. Este rápido reemplazo en caliente del código puede mejorar en gran medida la eficiencia del desarrollo y reducir el tiempo de desarrollo y depuración.
  4. Compatibilidad : DCEVM es compatible con JVM estándar y se puede utilizar con múltiples implementaciones de JVM. Se puede integrar con OpenJDK, Oracle JDK y otras JVM para proporcionar mejoras en las aplicaciones.
  5. Implementación y configuración : el uso de DCEVM requiere configurarlo como una versión alternativa de JVM e integrarlo con la aplicación de destino. DCEVM proporciona las correspondientes herramientas de instalación y distribución binaria, lo que hace que la implementación y configuración sean relativamente simples.

En resumen, DCEVM es una herramienta que mejora la JVM y permite a los desarrolladores cargar y modificar dinámicamente archivos de clase Java en tiempo de ejecución al proporcionar capacidades de implementación en caliente y desarrollo rápido. Puede realizar el reemplazo de código activo con efecto inmediato, mejorando la eficiencia y la flexibilidad del desarrollo. DCEVM es compatible con JVM estándar y se puede integrar con múltiples JVM, lo que brinda a los desarrolladores una herramienta conveniente y poderosa para acelerar el proceso de desarrollo y depuración de aplicaciones Java.

4. Análisis de la tecnología Sonic Hot Deployment

(1) Modelo de arquitectura general de Sonic

(2) Transferencia de función sónica

Sonic monitorea los cambios de archivos locales a través de NIO y activa eventos de cambio de archivos, como adición de clases, modificación de clases, recarga de Spring Bean y otros procesos de eventos. La siguiente figura muestra el ciclo de vida de un único archivo durante una implementación en caliente:

(3) Monitoreo de archivos

Sonic primero predefinirá dos directorios, local y /var/tmp/sonic/extraClasspathremoto /var/tmp/sonic/classes. extraClasspath es la URL Classpath expandida personalizada de Sonic, y clases es el directorio que Sonic monitorea. Cuando se cambian los archivos, se implementan en ubicaciones remotas/locales a través del complemento IDEA, lo que activa el directorio de monitoreo del Agente para continuar con la siguiente lógica de recarga en caliente:

¿Por qué Sonic no reemplaza directamente los archivos de recursos en el ClassPath del usuario?

Porque considerando que los proyectos API del paquete WAR del lado comercial, Spring Boot, proyecto Tomcat, proyecto Jetty, etc. se inician con paquetes JAR, el archivo Class del usuario no se puede modificar directamente. Incluso si el proyecto del usuario se puede modificar, operar directamente la Clase del usuario generará una serie de problemas de seguridad.

Por lo tanto, Sonic utiliza rutas URL ClassPath ampliadas para modificar y agregar archivos. Y existe un escenario en el que varios proyectos del lado empresarial introducen el mismo paquete JAR y configuran el XML y las anotaciones de MyBatis en el JAR. En este caso, Sonic no tiene forma de modificar directamente los archivos fuente en el paquete JAR. Al expandir la ruta, puede modificar un determinado archivo y XML en el paquete JAR sin prestar atención al paquete JAR. De la misma manera, este método se puede utilizar para reemplazar en caliente todo el paquete JAR. Presentemos brevemente el oyente principal de Sonic, como se muestra en la siguiente figura:

(4) Recarga de clase JVM

La lógica de recarga por lotes de código de bytes de JVM genera definiciones de ClassDefinition e instrumentación.redefineClasses (definiciones) a través del nuevo flujo binario de código de bytes y el antiguo objeto Class para activar la recarga de JVM. Después de la sobrecarga, el registro del complemento Spring se activará durante la inicialización. Transfrom. A continuación, explicamos brevemente cómo se sobrecarga Spring.

¿Cómo garantizar que la clase Sonic recién agregada se pueda cargar en el contexto del cargador de clases? Dado que el proyecto se ejecuta de forma remota, el entorno de ejecución es complejo. Puede iniciarse en modo de paquete JAR (Spring Boot), puede ser un proyecto normal o puede ser un proyecto War Web. Para tales situaciones, Sonic ha creado un capa de expansión de URL de Classloader.

User ClassLoader es el nombre colectivo del ClassLoader personalizado del marco. Por ejemplo, el proyecto Jetty es WebAppclassLoader. Urlclasspath es el archivo lib del proyecto actual. Por ejemplo, el proyecto Spring Boot también carga CLass desde la ruta BOOT-INF/lib/ del proyecto actual, etc. Las ubicaciones personalizadas de diferentes marcos son ligeramente diferentes. Por lo tanto, para este tipo de situación, el Agente debe obtener el Classloader personalizado del usuario. Si se inicia de forma convencional, como un proyecto Spring XML normal, publicado con la ayuda de Plus (la plataforma de publicación de servicios interna de Meituan), hay No hay un cargador de clases personalizado para este tipo, que es el AppClassLoader predeterminado, por lo que el Agente utiliza la mejora del código de bytes para obtener el cargador de clases del usuario real durante el proceso de inicio del proyecto del usuario.

Después de encontrar el subcargador de clases utilizado por el usuario, obtenga el elemento Classpath en el cargador de clases a través de la reflexión. La URL en ClassPath son todos los entornos de clase en tiempo de ejecución necesarios cuando el proyecto actual carga la clase, e incluye dependencias de paquetes JAR de terceros, etc. .

Sonic obtiene la matriz de URL y agrega el directorio Classpath extendido personalizado de Sonic al primer lugar en la matriz de URL. De esta manera, cuando se agrega una nueva clase, Sonic solo necesita copiar el archivo de clase al directorio del paquete correspondiente al Classpath extendido. Cuando hay otros Beans Cuando se confía en una clase recién agregada, el archivo de clase se buscará desde el directorio actual.

¿Por qué no simplemente fortalecer Appclassloader? ¿Pero para fortalecer el cargador de clases personalizado del marco?

Considere tal escenario. Hay ClassA en el cargador de clases personalizado del marco. En este momento, el nuevo ClassB del usuario necesita cargarse en caliente. La clase B tiene una relación de referencia con A. Si se mejora AppClassLoader, ClassLoader se inicializa cuando se abre la instancia B. inicializado. loadclass primero carga el código de bytes de ClassB desde UserClassLoader. Confiando en el principio de delegación parental, B es cargado por Appclassloader. Debido a que B depende de la clase A, el AppClassLoader que carga B actualmente no debe poder cargarlo. En este momento, se genera una excepción ClassNotFoundException. Se lanzará una excepción. Por lo tanto, al expandir el cargador de clases, se debe expandir el cargador de clases de nivel superior para lograr el efecto que desea el usuario.

(5) Sobrecarga de Spring Bean

Durante el proceso de recarga de Spring Bean, el proceso de destrucción y reinicio de Bean, el contenido principal se muestra en la siguiente figura:

Cómo cargar Spring Beans

Primero, al modificar Java Clase D, escanee y verifique si el Bean modificado actualmente es un Spring Bean (verificación de anotaciones) a través de Spring ClasspathScan, y luego active el proceso de destrucción (BeanDefinitionRegistry.removeBeanDefinition). Este método eliminará el Bean D y las dependencias en el Contexto actual de Spring. El Bean C del Spring Bean D también se destruye, pero el alcance es solo en el contexto actual de Spring. Si C depende del Bean B en el subcontexto, la relación de dependencia en el subcontexto no se puede actualizar. Cuando hay una solicitud del sistema, el Bean C asociado en el Bean B sigue siendo el objeto antes de la implementación en caliente, por lo que la implementación en caliente falla.

Por lo tanto, durante el proceso de inicialización de Spring, se debe mantener la relación correspondiente entre los contextos padre e hijo. Cuando el contexto hijo cambia, si el alcance del cambio involucra al Bean B, las dependencias en el contexto hijo deben actualizarse nuevamente. Si hay varios contextos asociados, es necesario mantener varios contextos y la entrada del entorno del contexto actual requiere recargar. Las entradas aquí se refieren a: Spring MVC Controller, Mthrift y Pigeon. Se utilizan diferentes estrategias de recarga para diferentes entradas de tráfico. Las operaciones principales de la entrada del marco RPC son desvincular el centro de registro, volver a registrarse, recargar el proceso de inicio, etc. Para el controlador Spring MVC, las operaciones principales son desvincular y registrar el mapeo de URL para implementar cambios en la entrada del tráfico. clase.

(6) Sobrecarga de Spring XML

Cuando los usuarios modifican/agregan Spring XML, todos los beans en el XML deben recargarse.

Después de recargar, destruye Spring y reinícialo. Cabe señalar que el método de modificación XML ha sufrido cambios importantes, que pueden involucrar la configuración global de AOP y el contenido relacionado antes y después del procesador. El alcance del impacto es global, por lo que actualmente solo se permite agregar etiquetas XML Bean ordinarias. /Modificación, otras habilidades se irán liberando gradualmente según corresponda.

(7) Implementación en caliente de MyBatis

El principal proceso de procesamiento de la implementación en caliente de Spring MyBatis es obtener todas las rutas de configuración durante el inicio, mantener su correspondencia con Spring Context, hacer coincidir la configuración cuando se implementa en caliente Class y XML, y luego recargar la configuración para lograr el propósito de la implementación en caliente.

5. Aplicación sónica

(1) Descripción general de las funciones de implementación en caliente

Además del proceso de recarga de Spring Bean, Spring MVC y MyBatis, Sonic también admite otros marcos de desarrollo de uso común. El soporte y la compatibilidad de marcos enriquecidos son la piedra angular de Sonic. A continuación se muestran algunos marcos de terceros de uso común compatibles con Sonic:

Hasta ahora, Sonic ha admitido la carga en caliente de los marcos de terceros más utilizados y casi no hay necesidad de reiniciar los servicios para el desarrollo empresarial de rutina. Y la tasa de éxito dentro de Meituan ha alcanzado más del 99,9%, lo que hace realmente posible que la implementación en caliente reemplace la implementación y la construcción convencionales.

(2) Integración del complemento IDE

Sonic también proporciona un potente complemento IDEA, que permite a los usuarios realizar un desarrollo inmersivo y la implementación remota en caliente se vuelve más conveniente.

Enlaces de referencia

  1. Serie Java | Práctica de implementación de implementación remota en caliente en Meituan - Equipo técnico de Meituan : principales materiales de aprendizaje
  2. "Tutorial de instrumentación y agente Java de Baeldung": el conocido sitio web para desarrolladores de Java de Baeldung ofrece tutoriales y códigos de muestra sobre la instrumentación y el agente Java. Puede buscar tutoriales relevantes en el sitio web de Baeldung, como "Tutorial del Agente Java Baeldung".
  3. "Documentación oficial de Oracle": Oracle proporciona documentación oficial para Java SE. Puede encontrar instrucciones detalladas y ejemplos para la API de instrumentación y el Agente Java en el sitio web oficial de Oracle. Visite el sitio web oficial de Oracle y busque "documentación de Java SE" para ingresar a la página de documentación oficial.
  4. "Documentación oficial de Byte Buddy": la popular biblioteca de manipulación de códigos de bytes de Java de Byte Buddy, que se utiliza ampliamente para desarrollar agentes Java. Puede visitar el sitio web oficial de Byte Buddy y encontrar guías detalladas y ejemplos sobre el uso de Byte Buddy y la API de instrumentación en el sitio web.

Supongo que te gusta

Origin blog.csdn.net/xiaofeng10330111/article/details/131026729
Recomendado
Clasificación