micronaut
< < Articles

Micronaut : Quelles nouveautés en cette année 2019 ?

30/09/2019

Une première présentation du framework Micronaut a été réalisée par Yohan et Chloé en octobre 2018 pour exposer les bases du framework. L’objectif de ce premier article était de montrer la simplicité de réalisation d’une découverte de services et du traçage de requêtes ; le tout conçu sur de la programmation réactive de bout en bout. Je vous invite donc fortement à aller lire cet article qui vous permettra d’avoir une vue globale de ce framework.

Pour rappel, Micronaut est un framework libre supporté officiellement par OCI (sponsor officiel de Grails) depuis le 23 mai 2018. Ce framework est calibré pour les architectures microservices, réactif par nature et conçu avec un système de compilation AOT (Ahead-of-Time). Cette technique de compilation évite au maximum la réflexion lors de l’exécution, ce qui permet un temps de démarrage minimal au détriment d’un temps de compilation plus conséquent.

À la suite de l’étude réalisée dans l’article de présentation du framework, quelques limitations ont tout de même été rencontrées :

  • L’obligation de passer par une implémentation manuelle du repository lors de la persistance de données avec Hibernate ;
  • L’annotation @Sql manquante, ce qui introduit un peu de boilerplate dans l’écriture des tests ;
  • Le support non officiel du JDK 11.

Nous allons donc voir si nos attentes ont été comblées dans les dernières versions de Micronaut.

L’ensemble de ce que nous allons étudier par la suite est présent dans la version 1.1.0 du projet Github micronaut-demo. Ce projet a d’ailleurs été le support du premier article de présentation du framework.

Micronaut Data

Une de nos principales attentes portait sur la gestion de la persistance de données. Quand on a l’habitude de l’écosystème Spring Boot et Spring Data, on s’attend à avoir des annotations “magiques” qui nous demande le minimum d’effort pour avoir une gestion simple de notre interface avec la base de données.

Micronaut vient donc à notre rescousse avec une solution clés en main : Micronaut Data. Cette dépendance nous permet d’utiliser l’annotation @Repository avec l’interface JpaRepository qui implémente l’ensemble des méthodes simples et nécessaires pour requêter notre base de données. Évidemment, ils ont aussi pensé au PageableRepository, nécessaire pour toute requête avec pagination. Comme dit plus haut, Micronaut est un framework réactif par nature, il était donc indispensable de proposer une solution réactive aux accès en base de données. C’est pourquoi ils ont introduit les interfaces suivantes :

  • AsyncCrudRepository qui retourne un CompletableFuture<T> ;
  • ReactiveStreamsCrudRepository qui retourne un Publisher<T>, ce qui nous permet de retourner des Flux<T> et Mono<T> grâce aux méthode Flux<T> from(Publisher<? extends T> source) et Mono<T> from(Publisher<? extends T> source) proposés par Reactor ;
  • RxJavaCrudRepository qui retourne des Single<T> et Flowable<T> de la librairie RxJava 2.

En plus de JPA, Micronaut Data supporte le SQL natif via l’implémentation Micronaut Data JDBC. Micronaut Data JDBC propose une interface JdbcRepository qui supporte une grande partie des possibilités de JPA comme la pagination, la projection (par attribut ou par DTO) ou encore les modifications par lot (ou “batch updates”). Micronaut Data JDBC n’est pas un ORM et ne propose donc pas certaines fonctionnalités comme le lazy loading ou l’optimistic locking, mais dans certains cas particuliers la simplicité de JDBC est amplement suffisante. Pour l’instant, Micronaut Data supporte le SQL natif uniquement via l’interface JDBC mais à terme cela pourrait évoluer, on pourrait imaginer avoir le support de R2DBC pour exécuter du SQL de façon réactive.

On pourrait se demander, tout de même, quelle est la plus-value par rapport a Spring Data ? Il est évident que Micronaut ne cache pas son inspiration de Spring Data mais ils ont su ajouter un élément fondamental de conception qui fait toute la différence. Comme dit plus haut, Micronaut est conçu avec un système de compilation AOT ce qui signifie qu’on veut charger le maximum de chose à la compilation et éviter la réflexion à l’exécution. Micronaut Data a donc été conçu pour être totalement compatible avec ce système de compilation. Ainsi on évite l’ensemble des points suivants à l’exécution :

  • pas de génération de meta-model qui utilise de la réflexion à outrance ;
  • pas de traduction des méthodes de repository en requête SQL à l’exécution ;
  • pas de réflexion ou de système de proxy.

Tout est fait à la compilation, ce qui permet à la fois une empreinte mémoire faible et une exécution rapide. Ainsi, on s’assure à la compilation de la bonne implémentation des méthodes de repository et on détecte les erreurs au plus tôt. De plus, qui dit compatibilité avec la compilation AOT, dit compatibilité avec GraalVM. Il est donc possible de créer plus simplement l’image native de notre application interagissant avec notre base de données. Petit aparté : historiquement le projet Micronaut Data s’appelait Micronaut Predator pour Precomputed Data Repositories, qui était directement lié à cet aspect de résolution à la compilation mais un nom plus consensuel a finalement été adopté.

Au niveau des tests, on regrettera de ne pas avoir d’annotation @Sql pour charger les données. On est donc toujours obligé d’écrire du code spécifique pour injecter nos données, soit en utilisant l’EntityManager comme défini dans la classe AbstractIT, soit en utilisant les méthodes de repository fournit par Micronaut Data. Cela viendra peut-être dans une version future : à ce jour Micronaut Data n’est qu’en version 1.0.0.M1.

Les avantages des dernières versions du framework

Outre le fait d’avoir ajouté Micronaut Data, qui est un vrai pas en avant pour devenir un framework complet et prêt pour la prod, Micronaut a ajouté différentes améliorations importantes.

Java

On appréciera donc le fait que le JDK 11 soit officiellement supporté. Nous n’avons plus le warning remonté dans l’article précédent :

warning: Supported source version 'RELEASE_8' from annotation processor
'org.gradle.api.internal.tasks.compile.processing.NonIncrementalProcessor'
less than -source '11'

Flyway

L’intégration de Flyway a été simplifiée par l’ajout de la librairie micronaut-flyway, qui permet de configurer simplement notre gestionnaire de migration de base de données en ajoutant simplement ce point de configuration dans notre application.yml :

flyway:
  datasources:
    default:
      locations: classpath:/db/migration, classpath:/db/data

Au passage, on peut configurer un endpoint qui nous fournit des informations sur la/les migration(s) exécutée(s) sur la base de données. Voici le point de configuration qui nous permet cela :

endpoints:
  flyway:
    enabled: true
    sensitive: false

NB : l’attribut sensitive permet de définir si le endpoint nécessite une authentification pour y accéder, attention à son activation en prod !

Pour finir, micronaut-flyway propose de souscrire à des évènements liés aux actions Flyway comme SchemaCleanedEvent lorsque le schéma a été vidé et MigrationFinishedEvent lorsque la migration s’est terminée.

BOM Micronaut

Au niveau du BOM Micronaut, celui-ci est continuellement mis à jour, que ce soit avec des montées de version ou des ajouts de nouvelles dépendances. Par exemple on appréciera de trouver le support de :

Et pour des besoins plus spécifiques, on retrouve le support de Kafka, MongoDB, AWS Lambda ou Kubernetes.

Vous pouvez retrouver la liste non exhaustive de l’ensemble des dépendances gérées dans le BOM Micronaut, dans la partie « RSS 2.0 Module Included in BOM » du chapitre What's New?

Enfin, au sein même de ce BOM on retrouvera la dépendance micronaut-test qui permet de gérer la rédaction des tests dans notre application. Elle était déjà disponible, auparavant, mais sa version n’était pas gérée dans le BOM, ce qui est toujours appréciable.

GraalVM

En ce qui concerne le support de GraalVM, Micronaut est compatible avec les dernières versions de GraalVM (19.2.2 à ce jour). Ce qui permet de réduire drastiquement la complexité de la commande native-image pour générer l’image native de nos applications. Voici un exemple de lignes de commande native-image pour une application simple hello world :

Ancienne ligne de commande

native-image --no-server \
            --class-path build/libs/hello-micronaut-graal-0.1-all.jar \
           -H:ReflectionConfigurationFiles=build/reflect.json \
            -H:EnableURLProtocols=http \
           -H:IncludeResources="logback.xml|application.yml|META-INF/services/*.*" \
            -H:Name=hello-micronaut-graal \
            -H:Class=example.Application \
            -H:+ReportUnsupportedElementsAtRuntime \
            -H:+AllowVMInspection \
            -H:-UseServiceLoaderFeature \
--rerun-class-initialization-at-runtime='sun.security.jca.JCAUtil$CachedSecureRandomHolder,javax.net.ssl.SSLContext' \
--delay-class-initialization-to-runtime=io.netty.handler.codec.http.HttpObjectEncoder,io.netty.handler.codec.http.websocketx.WebSocket00FrameEncoder,io.netty.handler.ssl.util.ThreadLocalInsecureRandom,com.sun.jndi.dns.DnsClient

Nouvelle ligne de commande

native-image --no-server \
             -H:IncludeResources="logback.xml|application.yml" \
              -jar build/libs/hello-micronaut-graal-0.1-all.jar \
              hello-micronaut-graal

Le gain en lisibilité est immédiat mais cette simplification n’est pas magique. Celle-ci est directement liée au fait que GraalVM initialise les classes à l’exécution plutôt qu’à la compilation depuis la version 19.0. Ainsi, pour obtenir l’image native optimale d’une application plus conséquente, il faudra tout de même rajouter certains paramètres à la commande native-image.

Pour simplifier la génération d’une image native, Micronaut propose plusieurs annotations pour générer le fichier reflection-config.json, qui contient l’ensemble des classes nécessitant de la réflexion à l’exécution :

  • @Introspected qui permet de gérer de l’introspection à la compilation et autorise de l’accès par réflexion à l’exécution ;
  • @TypeHint qui autorise uniquement de l’accès par réflexion à l’exécution.

Au travers de ces annotations, nous pouvons définir l’ensemble des classes, superclasses et packages qui nécessitent de la réflexion et qui doivent être renseignés lors de la phase de compilation. Évidemment, Micronaut se charge de renseigner ces annotations pour les modules qu’il gère, comme par exemple pour Jackson. Pour plus d’informations, vous pouvez vous référer à la documentation Micronaut et GraalVM.

Outre le support de la commande native-image, on notera que Micrometer est maintenant fonctionnel avec GraalVM. Ce qui est un vrai plus pour surveiller l’exécution de notre application en mode natif.

Les performances des applications “hello world” restent quasiment identiques à celles observées dans l’article de présentation de Micronaut. Ainsi nous observons un temps de démarrage d’environ 4 s pour l’application SpringBoot, 1,5 s pour l’application Micronaut et 35 ms pour l’application native. L’empreinte mémoire reste inchangée à ceci près qu’on remarque une légère augmentation de la taille de l’image native (on est passé de 44 Mo à 51 Mo) surement liée à l’ajout de Micrometer. On peut donc commencer à identifier un vrai gain pour les applications qui nécessitent un temps de démarrage rapide et une empreinte mémoire réduite (par exemple, pour les applications serverless).

Conclusion

Nous avons pu voir dans cet article que le framework Micronaut a bien mûri en un an. Il a su poser les bases d’un framework léger et calibré pour les microservices tout en évoluant avec son temps en ajoutant petit à petit les éléments inhérents à un écosystème microservice (Micronaut Data, AWS, MongoDB, Kubernetes,…)

Sans avoir la prétention de détrôner Spring Boot, Micronaut a su apporter un vent de fraîcheur dans l’univers des frameworks web Java (et Kotlin), en misant sur des solutions plus adaptées aux problématiques d’aujourd’hui (compilation AOT, support de GraalVM 19.2.x).

Je pense que Micronaut a maintenant de bonnes cordes à son arc pour s’imposer dans l’univers des frameworks web et j’espère qu’il va réussir à se faire une place, face à une solution plus récente comme Quarkus qui fait de plus en plus parler d’elle.