Quarkus et les Google Cloud Functions
Quarkus est un framework de développement de microservice pensé pour le cloud et les conteneurs.
Il est pensé pour avoir une utilisation mémoire réduite et un temps de démarrage le plus court possible.
Il se base principalement sur des standards (Jakarta EE, Eclipse MicroProfile, …) et permet l’utilisation de librairies Java matures et très répandues via ses extensions (Hibernate, RESTeasy, Vert.X, Kafka, …).
Quarkus a été pensé pour le cloud dès sa conception, il permet le développement d’applications Cloud Ready (tel que défini par le principe des applications 12 Factors ) et Cloud Native (utilisation des capacités des cloud publics pour développer vos applications).
Quarkus permet entre autres la création de fonctions Google Cloud Functions pour le développement de vos applications.
Quarkus permet d’implémenter une fonction Google Cloud Function de type background ou HTTP de plusieurs manières :
- Via les API de Google Cloud.
- Via Funqy : une framework de développement de fonction agnostique du fournisseur de Cloud.
- Via une des extensions HTTP de Quarkus: RESTEasy (JAX-RS), Vert.x reactive routes, Undertow (Servlet), Spring Web.
Background Function avec l’API Google Cloud
Première étape, créer un projet Quarkus avec le plugin Maven de Quarkus :
mvn io.quarkus:quarkus-maven-plugin:1.12.2.Final:create \ -DprojectGroupId=org.acme \ -DprojectArtifactId=google-cloud-functions \ -Dextensions="resteasy,google-cloud-functions"
Cette commande Maven va vous créer un projet contenant une fonction background et une fonction HTTP.
La fonction background créée est la suivante :
@ApplicationScoped //1 public class HelloWorldBackgroundFunction implements BackgroundFunction<HelloWorldBackgroundFunction.StorageEvent> { //2 @Override public void accept(StorageEvent event, Context context) throws Exception { //3 System.out.println("Receive event on file: " + event.name); } public static class StorageEvent { public String name; } }
- Pour que Quarkus détecte votre fonction, elle doit être annotée avec une annotation CDI.
- La fonction utilise l’API de Google, ici elle étend
BackgroundFunction
. - La fonction sera déclenchée par un événement représenté par l’objet
StorageEvent
, pour plus de simplicité j’ai uniquement défini sa propriété name.
Cette fonction sera déclenchée par un événement de type Google Cloud Storage, et va afficher dans ses logs le nom de l’objet créé ou mis à jour dans Cloud Storage.
Comme l’exemple vient avec deux fonctions, il vous faut supprimer la fonction HTTP qui a aussi été créée, ou sélectionner la fonction à déployer. Ces étapes sont expliquées dans le guide des Google Cloud Functions ici, et omises pour des raisons de simplicité.
Pour packager la fonction, vous devez utiliser la commande Maven suivante : mvn clean package
. Cette commande va créer un uberjar dans le répertoire target/deployment que vous pourrez ensuite déployer comme une Cloud Functions.
Pour déployer cet uberjar, vous pouvez utiliser la commande gcloud suivante :
gcloud beta functions deploy quarkus-example-storage \ --entry-point=io.quarkus.gcp.functions.QuarkusBackgroundFunction \ --trigger-resource quarkus-hello \ --trigger-event google.storage.object.finalize \ --runtime=java11 --source=target/deployment
Cette commande utilise comme entry point io.quarkus.gcp.functions.QuarkusBackgroundFunction
et non votre fonction. Cet entry point va démarrer Quarkus, puis localiser votre fonction dans le conteneur CDI.
Pour déclencher la fonction via une événement Cloud Storage, on doit définir l’événement de déclenchement google.storage.object.finalize
et préciser un nom de bucket, ici quarkus-hello. Ce bucket doit avoir été créé précédement via gsutil mb gs://quarkus-hello
ou la console Google Cloud.
L’ajout d’un objet dans le bucket quarkus-hello va alors déclencher votre fonction.
Vous pouvez tester cela avec la commande gsutil qui permet d’envoyer des fichiers dans un bucket Cloud Storage :
gsutil cp test.txt gs://quarkus-hello
Ou simuler un déclenchement via :
gcloud functions call quarkus-example-storage \ --data '{"name":"test.txt"}'.
On peut noter qu’on utilise la taille par défaut de fonction qui est de 256Mo, ceci est suffisant pour le démarrage du conteneur de la fonction (Jetty) et de notre application Quarkus. On tire parti de la consommation mémoire faible de Quarkus.
Si on regarde dans les logs de démarrage de notre fonction, on constate que notre fonction prend environ 2s à démarrer dont 1s pour Quarkus, et le reste (1s donc), pour le conteneur de fonction (Jetty). On tire parti du démarrage rapide de Quarkus.
HTTP Function avec l’API Google Cloud
Dans le projet Quarkus précédemment créé, une fonction HTTP a été générée dont voici le code :
@ApplicationScoped //1 public class HelloWorldHttpFunction implements HttpFunction { //2 @Override public void service(HttpRequest httpRequest, HttpResponse httpResponse) //3 throws Exception { Writer writer = httpResponse.getWriter(); writer.write("Hello World"); } }
- Pour que Quarkus détecte votre fonction, elle doit être annotée avec une annotation CDI.
- La fonction utilise l’API de Google, ici elle étend
HttpFunction
. - Pour implémenter notre fonction, on a accès à la requête et la réponse HTTP.
Cette fonction fait un simple Hello World.
Pour packager la fonction, vous devez utiliser la commande Maven suivante : mvn clean package
. Cette commande va créer un uberjar dans le répertoire target/deployment que vous pourrez ensuite déployer comme une Cloud Functions.
Pour déployer cet uberjar vous pouvez utiliser la commande gcloud suivante :
gcloud beta functions deploy quarkus-example-http \ --entry-point=io.quarkus.gcp.functions.QuarkusHttpFunction \ --runtime=java11 --trigger-http --source=target/deployment
Cette commande utilise comme entry point io.quarkus.gcp.functions.QuarkusHttpFunction
et non votre fonction. Cet entry point va démarrer Quarkus puis localiser votre fonction dans le conteneur CDI.
Pour plus d’informations sur les fonctions background et HTTP via l’API Google, voir le guide suivant : Quarkus Google Cloud Functions.
Background Function avec Funqy
L’exemple de fonction background que nous avons vu précédemment a comme inconvénient d’être dépendant de l’API Google Cloud Functions. Elle n’est donc pas portable d’un cloud à l’autre.
C’est là qu’entre en piste Funqy, l’API de fonction de Quarkus, agnostique du cloud provider. On peut réécrire la fonction background précédente via Funqy de la manière suivante :
public class HelloWorldBackgroundFunction { //1 @Funq //2 public void accept(StorageEvent event) throws Exception { //3 System.out.println("Receive event on file: " + event.name); } public static class StorageEvent { public String name; } }
- Votre fonction n’est plus dépendante de l’API de Google.
- L’annotation
@Funq
permet de dire à Funqy que c’est la méthode à appeler pour chaque déclenchement de fonction. - La fonction sera déclenchée par un événement représenté par l’objet
StorageEvent
, pour plus de simplicité j’ai uniquement défini sa propriété name.
Pour utiliser Funqy, il faut remplacer l’extensionquarkus-google-cloud-functions
par l’extension quarkus-funqy-google-cloud-functions
.
Après avoir packager votre application avec Maven, vous pouvez la déployer avec gcloud de la même manière que précédemment, sauf qu’il vous faut utiliser l’entry point io.quarkus.funqy.gcp.functions.FunqyBackgroundFunction
. Ce qui donne :
gcloud beta functions deploy quarkus-example-storage \ --entry-point=io.quarkus.funqy.gcp.functions.FunqyBackgroundFunction \ --trigger-resource quarkus-hello \ --trigger-event google.storage.object.finalize \ --runtime=java11 --source=target/deployment
Pour plus d’informations sur Funqy, voir le guide suivant : Quarkus Funqy Google Cloud Functions.
HTTP Function avec RESTEasy
L’exemple de fonction HTTP que nous avons vu précédemment a comme inconvénient d’être dépendant de l’API Google Cloud Functions. Elle n’est donc pas portable d’un cloud à l’autre.
De plus, la manipulation directe des objets request et response peut être fastidieuse.
Pour pallier à cela, Quarkus permet d’utiliser ses différentes extensions HTTP à la place de l’API de Google, votre fonction devient alors agnostique du cloud provider, et beaucoup plus simple à écrire.
L’exemple précédent peut être réécrit de la manière suivante en utilisant l’extension RESTEasy :
@Path("/") //1 public class HelloWorldHttpFunction{ //2 @GET //3 @Produces(MediaType.TEXT_PLAIN) public void String() throws Exception { return "Hello World"; } }
- On utilise ici l’annotation JAX-RS
@Path
permettant de définir la classe comme une ressource REST. - Notre classe ne dépend plus de l’API Google Functions.
- Notre fonction est implémentée via une opération REST classique (on peut même en définir plusieurs si nécessaire).
Pour utiliser RESTEasy, il faut remplacer l’extension quarkus-google-cloud-functions
par l’extension quarkus-google-cloud-functions-http
et ajouter l’extension RESTEasy : quarkus-resteasy-jackson
.
Après avoir packager votre application avec Maven, vous pouvez la déployer avec gcloud de la même manière que précédemment sauf qu’il vous faut utiliser l’entry point io.quarkus.gcp.functions.http.QuarkusHttpFunction
. Ce qui donne :
gcloud beta functions deploy quarkus-example-http \ --entry-point=io.quarkus.gcp.functions.http.QuarkusHttpFunction \ --runtime=java11 --trigger-http --source=target/deployment
Il est aussi possible d’écrire des fonctions HTTP avec les autres frameworks HTTP de Quarkus : reactive routes, Undertow (servlet), Funqy HTTP ou Spring Web.
Pour plus d’informations sur l’utilisation des extensions HTTP via des Cloud Functions, voir le guide suivant : Quarkus Google Cloud Function HTTP bindings.
Conclusion
Il est très facile de créer des fonctions déployables dans Google Cloud avec les extensions dédiés Quarkus. De plus Funqy (pour les fonctions brackground) et le binding HTTP (pour les fonctions HTTP) permettent de le faire sans que vôtre code soit liés à l’API de Google Cloud.
Les fonctions Google Cloud sont idéales pour l’utilisation des services Google Cloud, un pack d’extension existe pour faciliter leurs utilisations au sein de Quarkus via le pack d’extension Google Cloud Services qui permet d’utiliser BigQuery, BigTable, Firestore, PubSub, SecretManager, Spanner et Storage dans une application Quarkus.