Blog ENI : Toute la veille numérique !
🐠 -25€ dès 75€ 
+ 7 jours d'accès à la Bibliothèque Numérique ENI. Cliquez ici
Accès illimité 24h/24 à tous nos livres & vidéos ! 
Découvrez la Bibliothèque Numérique ENI. Cliquez ici
  1. Livres et vidéos
  2. Quarkus
  3. Compilation d’images natives avec GraalVM
Extrait - Quarkus Développer des applications microservices en Java pour le cloud et Kubernetes
Extraits du livre
Quarkus Développer des applications microservices en Java pour le cloud et Kubernetes Revenir à la page d'achat du livre

Compilation d’images natives avec GraalVM

Le projet GraalVM

Comme évoqué dans le chapitre Quarkus : carte d’identité, section Le support de GraalVM, GraalVM est un projet porté par Oracle Labs (division de recherche et développement d’Oracle) dans le but d’écrire une JVM en Java.

Le projet GraalVM comprend un compilateur de bytecode Java pouvant être utilisé en AOT (Ahead Of Time - avant l’exécution de l’application) ou en JIT (Just In Time - pendant l’exécution de l’application), l’outil native-image permettant de générer des images natives d’une application Java, et un framework de développement d’applications polyglottes appelé Truffle.

Le tout est distribué via GraalVM, une JVM basée sur OpenJDK mais qui remplace le JIT classique (C1/C2) par le compilateur Graal.

Une image native est un exécutable binaire d’une application optimisée pour une plateforme OS/CPU donnée. C’est le résultat de la compilation d’une application avec l’outil native-image. Un exécutable binaire ne peut être lancé que sur la plateforme OS/CPU avec laquelle il a été créé. GraalVM ne permet pas de cross-compilation à ce jour.

Même s’il est possible de réaliser des applications polyglottes avec Quarkus, le support de GraalVM...

La compilation native

Les projets générés par les outils fournis par Quarkus (le plugin Maven ou Gradle, la CLI, code.quarkus.io) vont tous contenir un profil Maven native qui va permettre la génération d’une image native de l’application lors du packaging de celle-ci.

La génération d’une image native à la place d’un JAR se fait donc en utilisant ce profil qui est activé via la propriété native.

  • Exemple avec Maven : mvn package -Dnative.

  • Exemple avec la CLI : quarkus build --native.

Pour compiler en natif une application, une installation de GraalVM est nécessaire. Le plugin de Quarkus supporte la distribution fournie par le projet GraalVM ainsi que la distribution Mandrel fournie par Red Hat.

Lors du packaging d’une application native, Quarkus recherche une distribution de GraalVM :

  • dans le path ;

  • dans la variable d’environnement JAVA_HOME ;

  • dans la variable d’environnement GRAALVM_HOME.

Si aucune n’est trouvée et que Docker est installé, Quarkus va utiliser une installation de GraalVM depuis une image Docker. C’est le moyen le plus simple d’utiliser GraalVM, chaque version de Quarkus ne supportant qu’un nombre réduit de versions de GraalVM (généralement une, voire deux), il peut être fastidieux d’avoir une configuration toujours à jour....

Les pièges et comment les contourner grâce à Quarkus

Il existe de nombreux pièges à éviter pour le développement d’une application compatible avec la compilation native. Heureusement, Quarkus les gère pour nous dans la plupart des cas.

Néanmoins, il arrive que certains patterns de développement posent des soucis avec les images natives. Le listing suivant contient trois problèmes relatifs à GraalVM (nous verrons dans la suite de ce chapitre comment les résoudre) :

@Path("/native") 
public class NativeImageResource { 
    private static final LocalDateTime STARTING_DATE = 
            LocalDateTime.now(); // <1> 
    private String message; 
 
    @PostConstruct 
    void loadMessage() throws IOException { 
        var cl = Thread.currentThread().getContextClassLoader(); 
        // <2> 
        try(var is = cl.getResourceAsStream("message.txt")){ 
            message = new String(is.readAllBytes()); 
        } 
    } 
 
    @GET 
    @Path("/hello") 
    public...

Tester une image native

Pour tester la ressource NativeImageResource, nous pouvons écrire le test suivant :

@QuarkusTest 
class NativeImageResourceTest { 
 
    @Test 
    void hello() { 
        when().get("/native/hello") 
                .then() 
                .statusCode(200) 
                .body(is(""" 
                        {"name":"Hello","message":"native image"}""")); 
    } 
 
    @Test 
    void message() { 
        when().get("/native/message") 
                .then() 
                .statusCode(200) 
                .body(is("Hello...

Monitorer et déboguer une image native

Comme nous l’avons vu en début de ce chapitre, une application native va inclure une JVM partielle appelée SubstratVM.

Celle-ci ne supporte pas JVM TI, le protocole utilisé par la plupart des outils de monitoring et de débogage de la JVM OpenJDK.

Il va donc falloir utiliser des outils différents pour ces besoins.

1. Génération d’un thread dump

Un thread dump est un instantané de tous les threads actifs à un instant donné. Les applications modernes utilisant souvent un grand nombre de threads, il est exploité pour analyser leur exécution et mieux comprendre le comportement d’une application.

Pour générer un thread dump d’une application native, il faut envoyer un signal SIGQUIT au processus de l’application native. On utilise généralement pour cela l’outil kill qui prend en paramètre le numéro du signal, 3 pour SIGQUIT.

Voici un exemple pour générer un thread dump pour une application native dont l’identifiant du processus (PID) est 99 : kill -3 99.

Le thread dump sera généré dans les logs de l’application.

2. Génération d’un heap dump

Un heap dump est un instantané de l’état de la mémoire heap à un instant donné. Il est utilisé pour analyser la mémoire...