Vers le natif : exemple de la technologie Spring&Dubbo AOT et explication du principe

Auteur : Liu Jun

À l'ère du cloud computing, les applications Java sont confrontées à des problèmes tels qu'un "démarrage à froid" lent, une utilisation élevée de la mémoire et un long temps de préchauffage, et ne peuvent pas bien s'adapter aux modes de déploiement cloud tels que Serverless. GraalVM résout ces problèmes dans une large mesure grâce à des technologies telles que la compilation statique et le packaging.Pour résoudre ces problèmes, et pour certaines restrictions d'utilisation de GraalVM, des frameworks grand public tels que Spring et Dubbo fournissent également des solutions AOT correspondantes.

Dans cet article, nous analyserons en détail les défis auxquels les applications Java sont confrontées à l'ère du cloud, comment GraalVM Native Image résout ces problèmes, les concepts de base et les principes de fonctionnement de GraalVM, et enfin nous montrerons comment intégrer un microservice commun avec un Spring6 + Exemple d'application de microservice Dubbo3. Les applications de service sont packagées de manière statique.

Cet article est principalement divisé en quatre parties :

  1. Tout d'abord, nous allons d'abord examiner les caractéristiques que les applications cloud devraient avoir dans le développement rapide actuel du cloud computing, et les défis auxquels les applications Java sont confrontées sur le cloud.
  2. Deuxièmement, je présenterai GraalVM, qu'est-ce que Native Image, et comment imprimer statiquement des applications Java via GraalVM pour générer des programmes binaires exécutables à partir de Native Image.
  3. Dans la troisième partie, nous savons que l'utilisation de GraalVM a certaines restrictions. Par exemple, les fonctionnalités dynamiques telles que la réflexion Java ne sont pas prises en charge, nous devons donc fournir une configuration spéciale des métadonnées pour contourner ces restrictions. Dans cette partie, nous expliquerons comment pour ajouter le traitement AOT est introduit pour réaliser la configuration automatique des métadonnées, y compris le traitement AOT dans le cadre Spring6, le traitement AOT dans le cadre Dubbo3, etc.
  4. Enfin, nous utiliserons un exemple d'application Spring6 + Dubbo3 pour montrer comment empaqueter statiquement une telle application Java.

Défis rencontrés par les applications Java à l'ère du cloud

Tout d'abord, examinons les caractéristiques des applications de l'ère du cloud computing et les défis auxquels Java est confronté à l'ère du cloud. À en juger par les données fournies par diverses agences statistiques, le langage Java est toujours l'un des langages de programmation les plus populaires pour les développeurs aujourd'hui, juste derrière certains langages de développement de scripts. Le langage Java peut être utilisé pour développer des applications métier de manière très efficace.La riche écologie confère à Java une efficacité de développement et de fonctionnement très élevée, et d'innombrables applications sont développées sur la base du langage Java.

image

Mais à l'ère du cloud computing, le déploiement et le fonctionnement des applications Java ont commencé à rencontrer de nombreux problèmes. Prenons l'exemple de Serverless. Serverless est un modèle de déploiement de plus en plus répandu sur le cloud. Il permet aux développeurs de se concentrer davantage sur la logique métier et d'aider à résoudre les problèmes de ressources grâce à une élasticité rapide. Selon les dernières données, Java est présent dans tous les cloud computing. La proportion du temps d'exécution sans serveur du fournisseur n'est pas élevé, loin de correspondre à sa proportion dans le développement d'applications traditionnelles.

image

La raison principale en est que les applications Java ne peuvent pas répondre à plusieurs exigences clés du scénario sans serveur.

  • Le premier est le problème de la vitesse de démarrage : le démarrage à froid de Java prend beaucoup de temps. Il s'agit d'un très gros défi pour les scénarios sans serveur qui nécessitent une fenêtre contextuelle rapide, car le temps d'extraction des applications Java peut être de l'ordre de quelques secondes ou dizaines de secondes ;
  • Le deuxième point est que les applications Java ont souvent besoin d'un certain temps de préchauffage pour atteindre le meilleur état de performances. Il n'est pas approprié d'allouer un trafic relativement important aux applications qui viennent d'être lancées, et des problèmes tels que les délais d'expiration des requêtes et les ressources élevées l'utilisation se produit souvent, ce qui prolonge encore le temps d'extraction effectif de l'application Java ;
  • Le troisième point concerne les exigences des applications Java sur l'environnement d'exploitation. Cela nécessite souvent de grandes ressources de mémoire et de calcul, mais celles-ci ne sont pas allouées à l'entreprise elle-même et sont consommées sur certains runtimes JVM. Ce n'est pas la même chose que d'utiliser le cloud. pour réduire les coûts.Les objectifs d'amélioration de l'efficacité sont combinés et assortis ;
  • Enfin, le package ou l'image créé par l'application Java est également très volumineux, ce qui affecte également l'efficacité du stockage et de la récupération dans son ensemble.

Voyons maintenant comment GraalVM, une technologie de packaging et d'exécution, résout ces problèmes rencontrés par les applications Java.

Introduction à GraaIVM

GraalVM compile vos applications Java à l'avance dans des binaires autonomes qui démarrent instantanément, offrent des performances optimales sans préchauffage et utilisent moins de ressources.

Selon l'introduction officielle, GraalVM fournit des capacités de compilation AOT et de packaging binaire pour les applications Java. Les packages binaires basés sur GraalVM peuvent atteindre un démarrage rapide, des performances ultra-élevées, aucun temps de préchauffage et très peu de consommation de ressources. L'AOT mentionné ici est une abréviation technique qui se produit lors de la compilation, c'est-à-dire Ahead-of-time, dont nous parlerons plus tard. En général, GraalVM peut être divisé en deux parties.

  • Tout d'abord, GraalVM est une version complète du JDK, à partir de là, il équivaut à OpenJDK et peut exécuter n'importe quelle application développée dans un langage orienté jvm ;
  • Deuxièmement, GraalVM fournit une technologie de packaging Native Image, qui peut empaqueter l'application dans un paquet binaire qui peut s'exécuter indépendamment.Ce paquet est une application autonome qui peut s'exécuter sans la JVM.

image.png

Comme le montre la figure ci-dessus, le compilateur GraalVM fournit deux modes, JIT et AOT.

  • Pour JIT, nous savons tous que les classes Java seront compilées dans des fichiers au format .class. Après compilation ici, c'est le bytecode reconnu par jvm. Lors de l'exécution d'applications Java, le compilateur JIT compilera du code Byte est compilé dans la machine code, qui a atteint une vitesse d'exécution plus rapide ;
  • Pour le mode AOT, il convertit directement le bytecode en code machine lors de la compilation, ce qui évite directement la dépendance vis-à-vis de jvm au moment de l'exécution.Étant donné que le temps de chargement de jvm et de préchauffage de l'exécution du bytecode est enregistré, les programmes AOT compilés et packagés ont une efficacité d'exécution très élevée.

image

En général, JIT permet aux applications d'avoir des capacités de traitement extrêmes plus élevées, ce qui peut réduire l'indicateur clé du délai maximal des requêtes ; tandis qu'AOT peut encore améliorer la vitesse de démarrage à froid des applications, avec des mentions de paquet binaire plus petites, Dans l'état d'exécution, moins ressources telles que la mémoire sont nécessaires.

Qu'est-ce que l'image native ?

Nous avons mentionné le concept d'image native dans GraalVM à plusieurs reprises ci-dessus. Native Image est une technologie qui compile et empaquete le code Java dans un programme binaire exécutable. Le paquet contient uniquement le code requis par le runtime, y compris le propre code de l'application et les dépendances standard. Code statique associé aux packages, aux runtimes de langage et aux bibliothèques JDK. Le fonctionnement de ce package ne nécessite plus l'environnement jvm. Bien entendu, il est lié à l'environnement machine spécifique et doit être empaqueté séparément pour différents environnements machine. Native Image dispose d'un ensemble de fonctionnalités répertoriées ici :

  • Ne contient qu'une partie des ressources nécessaires à l'exécution de la JVM, et le coût de fonctionnement est inférieur

  • Temps de démarrage en millisecondes

  • Il entre dans le meilleur état après le démarrage, pas besoin de se réchauffer

  • Peut être conditionné sous la forme d'un package binaire plus léger, ce qui rend le déploiement plus rapide et plus efficace

  • niveau de sécurité plus élevé

image

Pour résumer, voici les éléments clés : une vitesse de démarrage plus rapide, moins d'utilisation des ressources, moins de risques de vulnérabilités de sécurité et une taille de paquet binaire plus compacte. Résolvez les problèmes en suspens rencontrés par les applications Java dans les scénarios d'application de cloud computing tels que Sererless.

Le principe de base et l'utilisation de GraaIVM Native Image

Ensuite, jetons un coup d'œil à l'utilisation de base de GraalVM. Tout d'abord, vous devez installer les dépendances de base pertinentes requises par native-image, qui varient en fonction des différents environnements de système d'exploitation. Ensuite, vous pouvez utiliser le téléchargeur GraalVM JDK pour télécharger -image. Une fois que tout est installé, dans la deuxième étape, vous pouvez utiliser la commande native-image pour compiler et empaqueter des applications Java. L'entrée peut être des fichiers de classe, des fichiers jar, des modules Java, etc., et enfin empaqueté dans un fichier exécutable qui peut exécuter indépendamment, comme ici Hello World. De plus, GraalVM fournit également les plug-ins d'outils de construction Maven et Gradle correspondants pour faciliter le processus d'empaquetage.

image.png

GraalVM est basé sur un concept appelé "hypothèse du monde fermé", qui exige que toutes les ressources d'exécution et le comportement d'un programme puissent être entièrement déterminés lors de la compilation. La figure montre le processus spécifique de compilation et d'empaquetage AOT. Le code d'application gauche, l'entrepôt, le jdk, etc. sont tous utilisés comme entrée. GraalVM utilise main comme point d'entrée pour analyser tous les codes accessibles et les chemins d'exécution. Pendant le traitement, certains l'action de pré-initialisation, le code machine compilé AOT final et certaines données d'état telles que les ressources d'initialisation sont regroupés dans un package natif exécutable.

Comparé au mode de déploiement JVM traditionnel, le mode GraalVM Native Image est très différent.

  • GraalVM utilisera la fonction main comme point d'entrée lors de la compilation et de la construction pour compléter l'analyse statique du code de l'application
  • Le code qui ne peut pas être atteint lors de l'analyse statique sera supprimé et ne sera pas inclus dans le package binaire final
  • GraalVM ne peut pas reconnaître certains comportements d'appel dynamiques dans le code, tels que la réflexion, le chargement des ressources, la sérialisation, le proxy dynamique, etc., tous les comportements dynamiques seront limités
  • Classpath est solidifié pendant la phase de construction et ne peut pas être modifié
  • Le chargement de classe retardé n'est plus pris en charge, toutes les classes et le code disponibles sont déterminés au démarrage du programme
  • Il existe également d'autres fonctionnalités d'application Java dont l'utilisation est limitée (telles que l'initialisation de classe à l'avance, etc.)

GraalVM ne prend pas en charge les fonctionnalités dynamiques telles que la réflexion, mais bon nombre de nos applications et frameworks utilisent de nombreuses fonctionnalités telles que la réflexion et le proxy dynamique. Comment pouvons-nous empaqueter ces applications en tant qu'image native pour obtenir du statique ? GraalVM fournit une entrée de configuration de métadonnées. En fournissant des fichiers de configuration pour toutes les fonctionnalités dynamiques, le mode "hypothèse de monde fermé" est toujours établi, permettant à GraalVM de connaître tous les comportements attendus au moment de la compilation. Deux exemples sont donnés ici :

  1. En termes de méthodes d'encodage, telles que la méthode d'encodage reflétée ici, GraalVM peut calculer les métadonnées via l'analyse du code.

image

image

  1. Un autre exemple consiste à fournir des fichiers de configuration json supplémentaires et à les placer dans le répertoire spécifié META-INF/native-image//.

image

Traitement AOT

L'utilisation de fonctionnalités dynamiques telles que la réflexion dans les applications ou les frameworks Java est un obstacle qui affecte l'utilisation de GraalVM, et un grand nombre de frameworks ont cette limitation. Si des applications ou des développeurs sont tenus de fournir une configuration de métadonnées, ce sera un défi très difficile. Par conséquent, des frameworks tels que Spring et Dubbo ont introduit le traitement AOT, ou prétraitement AOT, avant la compilation AOT, ou compilation AOT. Le traitement AOT est utilisé pour compléter la collecte automatisée des métadonnées et fournir les métadonnées au compilateur AOT.

image

Le mécanisme de compilation AOT est commun à toutes les applications Java, mais par rapport à la compilation AOT, le processus de collecte des métadonnées par traitement AOT est différent pour chaque framework, car chaque framework a son propre usage pour la réflexion, le proxy dynamique, etc.

Prenons l'exemple d'une application de microservice Spring + Dubbo typique. Pour réaliser le packaging statique de cette application, cela implique le processus de traitement des métadonnées de Spring, Dubbo et un certain nombre de dépendances tierces.

  • Printemps - Traitement AOT du printemps
  • Dubbo - Traitement Dubbo AOT
  • Bibliothèques tierces - Métadonnées d'accessibilité

Pour Spring, le mécanisme Spring AOT a été publié dans Spring6 pour prendre en charge le prétraitement statique des applications Spring ; Dubbo a également publié récemment le mécanisme Dubbo AOT dans la version 3.2, permettant aux composants liés à Dubbo d'implémenter automatiquement le prétraitement natif ; en plus de cela, deux frameworks sont étroitement liés liés au développement de l'entreprise. Il existe souvent un grand nombre de dépendances tierces dans une application. Les métadonnées de ces dépendances sont également la clé pour affecter la statique. S'il y a des réflexions, un chargement de classe, etc., alors les métadonnées doivent leur être fourni Configuration, il existe actuellement deux canaux pour ces applications tierces, l'un est l'espace partagé officiellement fourni par GraalVM, où une partie considérable de la configuration des métadonnées dépendantes est disponible, et l'autre consiste à exiger la version officielle du composant pour inclure la configuration des métadonnées, GraalVM peut lire automatiquement les métadonnées dans les deux cas.

Configuration des métadonnées : https://github.com/oracle/graalvm-reachability-metadata

AO printemps

Examinons ensuite le travail de prétraitement effectué par Spring AOT avant la compilation.. Il existe de nombreuses fonctionnalités dynamiques dans le framework Spring, telles que la configuration automatique et les beans conditionnels. Spring AOT vise ces caractéristiques dynamiques, le prétraitement dans la phase de construction et la génération d'une série d'entrées de métadonnées pouvant être utilisées par GraalVM.Le contenu généré ici comprend :

  • Spring Bean définit la pré-génération de code associée, comme illustré dans la figure suivante
  • Générer du code lié au proxy dynamique pendant la phase de construction
  • À propos de certains fichiers de métadonnées JSON utilisés par la réflexion, etc.

image.png

Dubbo AOT

Ce que Dubbo AOT fait est très similaire à Spring AOT, sauf que Dubbo AOT est spécialement prétraité pour l'utilisation unique du framework Dubbo, notamment :

  • Génération de code source lié à l'extension SPI
  • Certaines réflexions utilisent des fichiers de configuration JSON pour générer
  • Génération de code de classe proxy RPC

image

image

Démo Spring6+Dubbo3

Ensuite, nous montrons comment utiliser Spring AOT, Dubbo AOT, etc. pour réaliser l'emballage de l'image native de l'application via un exemple d'application de microservice de Spring6 + Dubbo3.

L'exemple de code complet peut être téléchargé ici : https://github.com/apache/dubbo-samples/tree/master/1-basic/dubbo-samples-native-image

Étape 1 : Installer GraalVM

  1. Sélectionnez la version Graalvm correspondante selon votre propre système sur le site officiel Graalvm : https://www.graalvm.org/downloads/

  2. Installez native-image conformément à la documentation officielle : https://www.graalvm.org/latest/reference-manual/native-image/#install-native-image

Deuxième étape : Créer un projet

Cet exemple d'application est une application de microservice ordinaire et courante. Nous utilisons SpringBoot3 pour le développement de la configuration de l'application et Dubbo3 pour définir et publier des services RPC ; l'outil de construction d'application utilise Maven.

image.png

image.png

Étape 3 : Configurer le plug-in Maven

L'objectif est d'ajouter trois configurations de plug-in de spring-boot-maven-plugin, native-maven-plugin et dubbo-maven-plugin, d'activer le traitement AOT et de modifier la mainClass dans dubbo-maven-plugin au chemin complet de la classe de démarrage requise. (La méthode d'utilisation de l'API n'a pas besoin d'ajouter des dépendances 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>

Étape 4 : Ajouter des dépendances natives aux dépendances Pom

De plus, pour Dubbo, étant donné que certains mécanismes natifs actuels reposent sur JDK17 et d'autres versions, Dubbo n'intègre pas par défaut certains packages dans la version finale. Deux dépendances supplémentaires doivent donc être ajoutées : l'adaptation dubbo-spring6 et les composants dubbo-native.

<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>

Étape 5 : Ajuster le compilateur, le proxy, la sérialisation et l'enregistreur

Dans le même temps, la prise en charge de cet exemple pour les composants tiers est actuellement limitée, principalement les métadonnées d'accessibilité des composants tiers. Par exemple, les composants de communication ou de codage réseau actuellement pris en charge incluent Netty et Fastjson2 ; les composants de journal pris en charge sont Logback ; les composants de microservice incluent Nacos, Zookeeper, etc.

  • La méthode de sérialisation actuellement prise en charge est Fastjson2
  • Le compilateur et le proxy ne peuvent choisir que jdk pour le moment
  • l'enregistreur doit actuellement configurer slf4j, ne prend actuellement en charge que la déconnexion

Un exemple de configuration est le suivant :

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

Étape 6 : compiler

Exécutez la commande de compilation suivante sous le chemin racine du projet :

  • Exécuter directement par API
mvn clean install -P native -Dmaven.test.skip=true
  • Annotation et méthode xml (méthode intégrée Springboot3)
mvn clean install -P native native:compile -Dmaven.test.skip=true

Étape 7 : Exécutez le fichier binaire

Les fichiers binaires se trouvent dans le répertoire target/, et le nom du package binaire est généralement le nom du projet, tel que target/native-demo.

Résumer

La technologie GraalVM a apporté de nouveaux changements à l'application de Java à l'ère du cloud computing, aidant à résoudre le démarrage lent et l'occupation des ressources des applications Java, mais en même temps, nous avons également constaté certaines limitations dans l'utilisation de GraalVM, donc Spring6 , SpringBoot3 et Dubbo3 fournissent tous La solution native correspondante est fournie. En plus de Dubbo, Spring Cloud Alibaba fait également la promotion d'une solution d'emballage statique.Ensuite, nous allons promouvoir la vérification statique native globale autour des composants écologiques environnants des deux frameworks, tels que nacos, sentinel et seata.

Liens connexes:

[1] Blog Apache Dubbo

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

Les diplômés de l'Université populaire nationale ont volé les informations de tous les étudiants de l'école pour créer un site Web d'évaluation de la beauté et ont été détenus au criminel. La nouvelle version Windows de QQ basée sur l'architecture NT est officiellement publiée. Les États-Unis limiteront l'utilisation de la Chine d'Amazon, Microsoft et d'autres services cloud qui fournissent des modèles d'IA de formation.Des projets open source annoncés pour arrêter le développement de fonctions fonctions d'image du terminal Le nombre d'enregistrements de Threads a dépassé les 30 millions "Change" deepin adopte Asahi Linux pour s'adapter au classement de la base de données Apple M1 en juillet : Oracle surgit, ouvrant à nouveau le score
{{o.name}}
{{m.name}}

Acho que você gosta

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