Towards Native: ejemplo de tecnología Spring&Dubbo AOT y explicación de principio

Autor: Liu Jun

En la era de la computación en la nube, las aplicaciones Java se enfrentan a problemas como un "arranque en frío" lento, un uso elevado de la memoria y un tiempo de calentamiento prolongado, y no pueden adaptarse bien a los modos de implementación en la nube, como Serverless. GraalVM resuelve estos problemas en gran medida. a través de tecnologías como la compilación estática y el empaquetado.Para resolver estos problemas, y para algunas restricciones de uso de GraalVM, los marcos principales como Spring y Dubbo también brindan las soluciones AOT correspondientes.

En este artículo, analizaremos en detalle los desafíos que enfrentan las aplicaciones Java en la era de la nube, cómo GraalVM Native Image resuelve estos problemas, los conceptos básicos y los principios de funcionamiento de GraalVM y, finalmente, demostraremos cómo integrar un microservicio común con Spring6. + Ejemplo de aplicación de microservicio Dubbo3 Las aplicaciones de servicio se empaquetan estáticamente.

Este artículo se divide principalmente en las siguientes cuatro partes:

  1. En primer lugar, veremos las características que deben tener las aplicaciones en la nube en el rápido desarrollo actual de la computación en la nube y los desafíos que enfrentan las aplicaciones Java en la nube.
  2. En segundo lugar, presentaré GraalVM, qué es Native Image y cómo imprimir estáticamente aplicaciones Java a través de GraalVM para generar programas binarios ejecutables a partir de Native Image.
  3. En la tercera parte, sabemos que el uso de GraalVM tiene ciertas restricciones. Por ejemplo, las funciones dinámicas como la reflexión de Java no son compatibles, por lo que debemos proporcionar una configuración de metadatos especial para evitar estas restricciones. En esta parte, explicaremos cómo para agregar el procesamiento AOT se introduce para realizar la configuración automática de metadatos, incluido el procesamiento AOT en el marco Spring6, el procesamiento AOT en el marco Dubbo3, etc.
  4. Finalmente, usaremos un ejemplo de aplicación Spring6+Dubbo3 para demostrar cómo empaquetar estáticamente una aplicación Java de este tipo.

Desafíos que enfrentan las aplicaciones Java en la era de la nube

En primer lugar, echemos un vistazo a las características de las aplicaciones de la era de la computación en la nube y los desafíos que enfrenta Java en la era de la nube. A juzgar por los datos proporcionados por varias agencias estadísticas, el lenguaje Java sigue siendo uno de los lenguajes de programación más populares para los desarrolladores en la actualidad, solo superado por algunos lenguajes de desarrollo de secuencias de comandos. El lenguaje Java se puede usar para desarrollar aplicaciones comerciales de manera muy eficiente. La rica ecología hace que Java tenga una eficiencia de desarrollo y operación muy alta, y se desarrollan innumerables aplicaciones basadas en el lenguaje Java.

imagen

Pero en la era de la computación en la nube, la implementación y el funcionamiento de las aplicaciones Java comenzaron a enfrentar muchos problemas. Tomemos Serverless como ejemplo. Serverless es un modelo de implementación cada vez más común en la nube. Permite a los desarrolladores centrarse más en la lógica comercial y ayudar a resolver problemas de recursos a través de una elasticidad rápida. Según los datos más recientes, Java está en toda la computación en la nube. La proporción del tiempo de ejecución sin servidor del proveedor no es alto, lejos de igualar su proporción en el desarrollo de aplicaciones tradicionales.

imagen

La razón principal de esto es que las aplicaciones Java no pueden cumplir con varios requisitos clave del escenario sin servidor.

  • El primero es el problema de la velocidad de inicio: el inicio en frío de Java lleva mucho tiempo. Este es un desafío muy grande para los escenarios sin servidor que requieren una ventana emergente rápida, porque el tiempo de apertura de las aplicaciones Java puede ser del orden de segundos o decenas de segundos;
  • El segundo punto es que las aplicaciones Java a menudo necesitan una cierta cantidad de tiempo de calentamiento para lograr el mejor estado de rendimiento. No es apropiado asignar un tráfico relativamente grande a aplicaciones que acaban de iniciarse y problemas como tiempos de espera de solicitudes y recursos elevados. el uso a menudo ocurre, lo que prolonga aún más el tiempo efectivo de extracción de la aplicación Java;
  • El tercer punto son los requisitos de las aplicaciones Java en el entorno operativo. A menudo requiere una gran cantidad de memoria y recursos informáticos, pero estos no se asignan al negocio en sí y se consumen en algunos tiempos de ejecución de JVM. Esto no es lo mismo que usar la nube. para reducir costos Los objetivos de mejora de la eficiencia se combinan y combinan;
  • Finalmente, el paquete o la imagen creada por la aplicación Java también es muy grande, lo que también afecta la eficiencia del almacenamiento y la recuperación en su conjunto.

A continuación, echemos un vistazo a cómo GraalVM, una tecnología de empaquetado y tiempo de ejecución, resuelve estos problemas que enfrentan las aplicaciones Java.

Introducción a GraaIVM

GraalVM compila sus aplicaciones Java con anticipación en archivos binarios independientes que se inician instantáneamente, brindan el máximo rendimiento sin calentamiento y utilizan menos recursos.

Según la presentación oficial, GraalVM proporciona capacidades de compilación AOT y empaquetado binario para aplicaciones Java. Los paquetes binarios basados ​​en GraalVM pueden lograr un inicio rápido, un rendimiento ultraalto, sin tiempo de calentamiento y un consumo de recursos muy bajo. El AOT mencionado aquí es una abreviatura técnica que ocurre durante la compilación, es decir, Ahead-of-time, del cual hablaremos más adelante. En general, GraalVM se puede dividir en dos partes.

  • En primer lugar, GraalVM es una versión completa de lanzamiento de JDK, desde este punto es equivalente a OpenJDK y puede ejecutar cualquier aplicación desarrollada en un lenguaje orientado a jvm;
  • En segundo lugar, GraalVM proporciona tecnología de empaquetado de imágenes nativas, que puede empaquetar la aplicación en un paquete binario que puede ejecutarse de forma independiente.Este paquete es una aplicación autónoma que puede ejecutarse sin la JVM.

imagen.png

Como se muestra en la figura anterior, el compilador GraalVM proporciona dos modos, JIT y AOT.

  • Para JIT, todos sabemos que las clases de Java se compilarán en archivos en formato .class. Después de compilar aquí, es el código de bytes reconocido por jvm. Durante la ejecución de aplicaciones Java, el compilador JIT compilará algunos códigos de bytes que se compilan en la máquina. código, que ha logrado una mayor velocidad de ejecución;
  • Para el modo AOT, convierte directamente el código de bytes en código de máquina durante la compilación, lo que ahorra directamente la dependencia de jvm en el tiempo de ejecución.Dado que se ahorra el tiempo de carga de jvm y el calentamiento del tiempo de ejecución del código de bytes, los programas compilados y empaquetados de AOT tienen una eficiencia de tiempo de ejecución muy alta.

imagen

En general, JIT permite que las aplicaciones tengan capacidades de procesamiento extremas más altas, lo que puede reducir el indicador clave del retraso máximo de las solicitudes; mientras que AOT puede mejorar aún más la velocidad de inicio en frío de las aplicaciones, con menciones de paquetes binarios más pequeñas, en estado de ejecución, menos Se requieren recursos como la memoria.

¿Qué es Imagen Nativa?

Ya mencionamos el concepto de Imagen nativa en GraalVM muchas veces. Imagen nativa es una tecnología que compila y empaqueta código Java en un programa binario ejecutable. El paquete contiene solo el código requerido por el tiempo de ejecución, incluido el código propio de la aplicación y las dependencias estándar. Código estático asociado con paquetes, tiempos de ejecución de idiomas y bibliotecas JDK. El funcionamiento de este paquete ya no requiere el entorno jvm. Por supuesto, está vinculado al entorno de máquina específico y debe empaquetarse por separado para diferentes entornos de máquina. Imagen nativa tiene un conjunto de características enumeradas aquí:

  • Contiene solo una parte de los recursos requeridos por la JVM para ejecutarse, y el costo de ejecución es menor

  • Tiempo de inicio en milisegundos

  • Entra en el mejor estado después de comenzar, no es necesario calentar

  • Se puede empaquetar como un paquete binario más ligero, lo que hace que la implementación sea más rápida y eficiente

  • mayor nivel de seguridad

imagen

En resumen, estos son los elementos clave: mayor velocidad de inicio, menor uso de recursos, menor riesgo de vulnerabilidades de seguridad y un tamaño de paquete binario más compacto. Resuelva los problemas pendientes que enfrentan las aplicaciones Java en escenarios de aplicaciones de computación en la nube como Sererless.

El principio básico y el uso de GraaIVM Native Image

A continuación, echemos un vistazo al uso básico de GraalVM. Primero, debe instalar las dependencias básicas relevantes requeridas por native-image, que varían según los diferentes entornos del sistema operativo. A continuación, puede usar el descargador JDK de GraalVM para descargar aplicaciones nativas. -imagen. Después de instalar todo, en el segundo paso, puede usar el comando native-image para compilar y empaquetar aplicaciones Java. La entrada puede ser archivos de clase, archivos jar, módulos Java, etc., y finalmente empaquetados en un archivo ejecutable que puede ejecutarse de forma independiente, como aquí Hello World. Además, GraalVM también proporciona los correspondientes complementos de herramientas de compilación de Maven y Gradle para facilitar el proceso de empaquetado.

imagen.png

GraalVM se basa en un concepto llamado "suposición de mundo cerrado", que requiere que todos los recursos de tiempo de ejecución y el comportamiento de un programa puedan determinarse completamente durante la compilación. La figura muestra el proceso específico de compilación y empaquetado de AOT. El código de aplicación izquierdo, el almacén, jdk, etc. se utilizan como entrada. GraalVM utiliza main como punto de entrada para escanear todos los códigos accesibles y las rutas de ejecución. Durante el procesamiento, algunos la acción de preinicialización, el código de máquina compilado AOT final y algunos datos de estado, como los recursos de inicialización, se empaquetan en un paquete nativo ejecutable.

En comparación con el modo de implementación de JVM tradicional, el modo de imagen nativa de GraalVM es muy diferente.

  • GraalVM utilizará la función principal como punto de entrada durante la compilación y construcción para completar el análisis estático del código de la aplicación.
  • El código al que no se pueda acceder durante el análisis estático se eliminará y no se incluirá en el paquete binario final.
  • GraalVM no puede reconocer algunos comportamientos de llamadas dinámicas en el código, como la reflexión, la carga de recursos de recursos, la serialización, el proxy dinámico, etc., todos los comportamientos dinámicos estarán limitados
  • Classpath se solidifica durante la fase de construcción y no se puede modificar
  • Ya no se admite la carga de clase retrasada, todas las clases y el código disponibles se determinan al iniciar el programa
  • También hay algunas otras capacidades de la aplicación Java cuyo uso está restringido (como la inicialización de clase por adelantado, etc.)

GraalVM no admite funciones dinámicas como la reflexión, pero muchas de nuestras aplicaciones y marcos usan muchas funciones como la reflexión y el proxy dinámico. ¿Cómo podemos empaquetar estas aplicaciones como imagen nativa para lograr estática? GraalVM proporciona una entrada de configuración de metadatos Al proporcionar archivos de configuración para todas las características dinámicas, el modo de "suposición de mundo cerrado" aún se establece, lo que permite que GraalVM conozca todos los comportamientos esperados en el momento de la compilación. Aquí se dan dos ejemplos:

  1. En términos de métodos de codificación, como el método de codificación reflejado aquí, GraalVM puede calcular metadatos a través del análisis de código.

imagen

imagen

  1. Otro ejemplo es proporcionar archivos de configuración json adicionales y colocarlos en el directorio especificado META-INF/native-image//.

imagen

Procesamiento AOT

El uso de funciones dinámicas como la reflexión en aplicaciones Java o marcos es un obstáculo que afecta el uso de GraalVM, y una gran cantidad de marcos tienen esta limitación.Si se requiere que las aplicaciones o los desarrolladores proporcionen la configuración de metadatos, será un gran desafío Por lo tanto, los marcos como Spring y Dubbo han introducido el procesamiento AOT, o el preprocesamiento AOT, antes de la compilación AOT o la compilación AOT. El procesamiento AOT se utiliza para completar la recopilación automatizada de metadatos y proporcionar metadatos al compilador AOT.

imagen

El mecanismo de compilación AOT es común a todas las aplicaciones Java, pero en comparación con la compilación AOT, el proceso de recopilación de metadatos por parte del procesamiento AOT es diferente para cada marco, porque cada marco tiene su propio uso para la reflexión, el proxy dinámico, etc.

Tomemos como ejemplo una aplicación de microservicio típica de Spring + Dubbo. Para realizar el paquete estático de esta aplicación, involucra el proceso de procesamiento de metadatos de Spring, Dubbo y una serie de dependencias de terceros.

  • Primavera - Procesamiento AOT de primavera
  • Dubbo - Procesamiento Dubbo AOT
  • Bibliotecas de terceros: metadatos de accesibilidad

Para Spring, el mecanismo Spring AOT se lanzó en Spring6 para admitir el preprocesamiento estático de las aplicaciones Spring; Dubbo también lanzó el mecanismo Dubbo AOT en la versión 3.2 recientemente, lo que permite que los componentes relacionados con Dubbo implementen automáticamente el preprocesamiento nativo; además de esto, dos marcos están estrechamente relacionados con el desarrollo de negocios. A menudo hay una gran cantidad de dependencias de terceros en una aplicación. Los metadatos de estas dependencias también son la clave para afectar la estática. Si hay reflejos, carga de clases, etc. en ellos, entonces los metadatos deben para ellos Configuración, actualmente hay dos canales para estas aplicaciones de terceros, uno es el espacio compartido proporcionado oficialmente por GraalVM, donde una parte considerable de la configuración de Metadatos dependiente está disponible, y la otra forma es requerir el lanzamiento oficial del componente para incluir la configuración de metadatos, GraalVM puede leer metadatos automáticamente en ambos casos.

Configuración de metadatos: https://github.com/oracle/graalvm-reachability-metadata

Primavera AOT

A continuación, echemos un vistazo al trabajo de preprocesamiento realizado por Spring AOT antes de la compilación. Hay muchas características dinámicas en Spring Framework, como la configuración automática y los beans condicionales. Spring AOT está dirigido a estas características dinámicas, el preprocesamiento durante la fase de construcción y la generación de una serie de entradas de metadatos que GraalVM puede usar. El contenido generado aquí incluye:

  • Spring Bean define la pregeneración de código relacionado, como se muestra en la siguiente figura
  • Genere código relacionado con el proxy dinámico durante la fase de compilación
  • Acerca de algunos archivos de metadatos JSON utilizados por la reflexión, etc.

imagen.png

Dubbo AOT

Lo que hace Dubbo AOT es muy similar a Spring AOT, excepto que Dubbo AOT está especialmente preprocesado para el uso único del marco Dubbo, que incluye:

  • Generación de código fuente relacionado con la extensión SPI
  • Algunas reflexiones usan archivos de configuración JSON para generar
  • Generación de código de clase proxy RPC

imagen

imagen

Demostración de Spring6+Dubbo3

A continuación, demostramos cómo usar Spring AOT, Dubbo AOT, etc. para realizar el paquete de imagen nativa de la aplicación a través de una aplicación de microservicio de muestra de Spring6 + Dubbo3.

El ejemplo de código completo se puede descargar aquí: https://github.com/apache/dubbo-samples/tree/master/1-basic/dubbo-samples-native-image

Paso 1: Instalar GraalVM

  1. Seleccione la versión de Graalvm correspondiente según su propio sistema en el sitio web oficial de Graalvm: https://www.graalvm.org/downloads/

  2. Instale la imagen nativa de acuerdo con la documentación oficial: https://www.graalvm.org/latest/reference-manual/native-image/#install-native-image

Paso dos: crear un proyecto

Esta aplicación de muestra es una aplicación de microservicio común y corriente. Utilizamos SpringBoot3 para el desarrollo de la configuración de aplicaciones y Dubbo3 para definir y publicar servicios RPC; la herramienta de construcción de aplicaciones utiliza Maven.

imagen.png

imagen.png

Paso 3: Configure el complemento Maven

El enfoque es agregar tres configuraciones de plug-in de spring-boot-maven-plugin, native-maven-plugin y dubbo-maven-plugin, habilitar el procesamiento AOT y modificar mainClass en dubbo-maven-plugin a la ruta completa de la clase de inicio requerida. (El método de uso de API no necesita agregar dependencias spring-boot-maven-plugin).

<profiles>
        <profile>
            <id>native</id>
            <build>
                <plugins>
                    <plugin>
                        <artifactId>maven-compiler-plugin</artifactId>
                        <configuration>
                            <release>17</release>
                            <fork>true</fork>
                            <verbose>true</verbose>
                        </configuration>
                    </plugin>
                    <plugin>
                        <groupId>org.springframework.boot</groupId>
                        <artifactId>spring-boot-maven-plugin</artifactId>
                        <executions>
                            <execution>
                                <id>process-aot</id>
                                <goals>
                                    <goal>process-aot</goal>
                                </goals>
                            </execution>
                        </executions>
                    </plugin>
                    <plugin>
                        <groupId>org.graalvm.buildtools</groupId>
                        <artifactId>native-maven-plugin</artifactId>
                        <version>0.9.20</version>
                        <configuration>
                            <classesDirectory>${project.build.outputDirectory}</classesDirectory>
                            <metadataRepository>
                                <enabled>true</enabled>
                            </metadataRepository>
                            <requiredVersion>22.3</requiredVersion>
                        </configuration>
                        <executions>
                            <execution>
                                <id>add-reachability-metadata</id>
                                <goals>
                                    <goal>add-reachability-metadata</goal>
                                </goals>
                            </execution>
                        </executions>
                    </plugin>
                    <plugin>
                        <groupId>org.apache.dubbo</groupId>
                        <artifactId>dubbo-maven-plugin</artifactId>
                        <version>${dubbo.version}</version>
                        <configuration>
                            <mainClass>com.example.nativedemo.NativeDemoApplication</mainClass>
                        </configuration>
                        <executions>
                            <execution>
                                <phase>process-sources</phase>
                                <goals>
                                    <goal>dubbo-process-aot</goal>
                                </goals>
                            </execution>
                        </executions>
                    </plugin>
                </plugins>
            </build>
        </profile>
    </profiles>

Paso 4: agregue dependencias relacionadas con los nativos a las dependencias de Pom

Además, para Dubbo, debido a que algunos mecanismos nativos actuales se basan en JDK17 y otras versiones, Dubbo no empaqueta algunos paquetes en la versión de lanzamiento de forma predeterminada, por lo que se deben agregar dos dependencias adicionales: la adaptación de dubbo-spring6 y los componentes nativos de dubbo.

<dependency>
    <groupId>org.apache.dubbo</groupId>
    <artifactId>dubbo-config-spring6</artifactId>
    <version>${dubbo.version}</version>
</dependency>
<dependency>
    <groupId>org.apache.dubbo</groupId>
    <artifactId>dubbo-native</artifactId>
    <version>${dubbo.version}</version>
</dependency>

Paso 5: ajuste el compilador, el proxy, la serialización y el registrador

Al mismo tiempo, la compatibilidad de este ejemplo con componentes de terceros actualmente es limitada, principalmente los metadatos de accesibilidad de los componentes de terceros. Por ejemplo, los componentes de codificación o comunicación de red admitidos actualmente incluyen Netty y Fastjson2; los componentes de registro admitidos son Logback; los componentes de microservicio incluyen Nacos, Zookeeper, etc.

  • El método de serialización admitido actualmente es Fastjson2
  • El compilador y el proxy solo pueden elegir jdk en este momento
  • el registrador actualmente necesita configurar slf4j, actualmente solo admite el inicio de sesión

Un ejemplo de configuración es el siguiente:

dubbo:
  application:
    name: ${spring.application.name}
    logger: slf4j
    compiler: jdk
  protocol:
    name: dubbo
    port: -1
    serialization: fastjson2
  registry:
    id: zk-registry
    address: zookeeper://127.0.0.1:2181
  config-center:
    address: zookeeper://127.0.0.1:2181
  metadata-report:
    address: zookeeper://127.0.0.1:2181
  provider:
    proxy: jdk
    serialization: fastjson2
  consumer:
    proxy: jdk
    serialization: fastjson2

Paso 6: Compilar

Ejecute el siguiente comando de compilación en la ruta raíz del proyecto:

  • Ejecutar directamente por API
mvn clean install -P native -Dmaven.test.skip=true
  • Anotación y método xml (método integrado Springboot3)
mvn clean install -P native native:compile -Dmaven.test.skip=true

Paso 7: Ejecute el archivo binario

Los archivos binarios se encuentran en el directorio target/ y el nombre del paquete binario suele ser el nombre del proyecto, como target/native-demo.

Resumir

La tecnología GraalVM ha traído nuevos cambios a la aplicación de Java en la era de la computación en la nube, ayudando a solucionar el inicio lento y la ocupación de recursos de las aplicaciones Java, pero al mismo tiempo también hemos visto algunas limitaciones en el uso de GraalVM, por lo que Spring6 , SpringBoot3 y Dubbo3 proporcionan la solución nativa correspondiente. Además de Dubbo, Spring Cloud Alibaba también está promoviendo una solución de empaquetado estático A continuación, promoveremos la verificación estática nativa general en torno a los componentes ecológicos circundantes de los dos marcos, como nacos, centinela y seata.

Enlaces relacionados:

[1] Blog de Apache Dubbo

https://dubbo.apache.org/zh-cn/blog/

Los graduados de la Universidad Popular Nacional robaron la información de todos los estudiantes de la escuela para construir un sitio web de puntuación de belleza, y han sido detenidos criminalmente.La nueva versión de Windows de QQ basada en la arquitectura NT se lanza oficialmente.Estados Unidos restringirá el uso de China de Amazon, Microsoft y otros servicios en la nube que brindan capacitación en modelos de IA. Se anunciaron proyectos de código abierto para detener el desarrollo de funciones LeaferJS , el puesto técnico mejor pagado en 2023, lanzado: Visual Studio Code 1.80, una biblioteca de gráficos 2D de código abierto y potente , compatible funciones de imagen de terminal . El número de registros de subprocesos ha superado los 30 millones. "Cambio" deepin adopta Asahi Linux para adaptarse a la clasificación de la base de datos Apple M1 en julio: Oracle aumenta, abriendo el puntaje nuevamente
{{o.nombre}}
{{m.nombre}}

Supongo que te gusta

Origin my.oschina.net/u/3874284/blog/10087336
Recomendado
Clasificación