Création d'un microservice sur Quarkus, Kotlin et Gradle


Présentation


Dans l' article précédent, nous avons brièvement décrit le processus de création d'un microservice sur les frameworks JVM modernes, ainsi que leur comparaison. Cet article sera discuté plus en détail dans le Quarkus récemment publié par l'exemple de la création d'un microservice utilisant les technologies mentionnées et conformément aux exigences spécifiées dans l'article principal. L'application résultante fera partie de l'architecture de microservice suivante:


architecture cible


Le code source du projet, comme d'habitude, est disponible sur GitHub .


Avant de commencer à travailler avec un projet, les éléments suivants doivent être installés:



Créer un nouveau projet


Pour générer un nouveau projet, utilisez Web Starter ou Maven (pour créer un projet Maven ou un projet Gradle ). Il est à noter que le framework prend en charge les langages Java, Kotlin et Scala.


Dépendances


Dans ce projet, Gradle Kotlin DSL est utilisé comme système de construction. Le script de construction doit contenir:


  • plugins
    Listing 1. build.gradle.kts


    plugins { kotlin("jvm") kotlin("plugin.allopen") id("io.quarkus") } 

    La résolution de la version du plugin se fait dans settings.gradle.kts .


  • addictions
    Listing 2. build.gradle.kts


     dependencies { ... implementation(enforcedPlatform("io.quarkus:quarkus-bom:$quarkusVersion")) implementation("io.quarkus:quarkus-resteasy-jackson") implementation("io.quarkus:quarkus-rest-client") implementation("io.quarkus:quarkus-kotlin") implementation("io.quarkus:quarkus-config-yaml") testImplementation("io.quarkus:quarkus-junit5") ... } 

    Des informations sur l'importation de la nomenclature Maven sont disponibles dans la documentation Gradle .



Vous devez également ouvrir certaines classes Kotlin (elles sont final par défaut; plus d'informations sur la configuration de Gradle dans le guide Quarkus Kotlin :


Listing 3. build.gradle.kts


 allOpen { annotation("javax.enterprise.context.ApplicationScoped") } 

La configuration


Le cadre prend en charge la configuration à l'aide des propriétés et des fichiers YAML
(plus dans le guide de configuration de Quarkus ). Le fichier de configuration se trouve dans le dossier des resources et ressemble à ceci:


Listing 4. application.yaml


 quarkus: http: host: localhost port: 8084 application-info: name: quarkus-service framework: name: Quarkus release-year: 2019 

Dans ce fragment de code, les paramètres standard et personnalisés du microservice sont configurés. Ce dernier peut être lu comme ceci:


Listing 5. Lecture des paramètres d'application personnalisés ( code source )


 import io.quarkus.arc.config.ConfigProperties @ConfigProperties(prefix = "application-info") class ApplicationInfoProperties { lateinit var name: String lateinit var framework: FrameworkConfiguration class FrameworkConfiguration { lateinit var name: String lateinit var releaseYear: String } } 

Bacs


Avant de commencer à travailler avec le code, il convient de noter que dans le code source de l'application Quarkus, il n'y a pas de méthode principale (bien qu'elle puisse apparaître ).


@ConfigProperties bean @ConfigProperties de la liste précédente dans un autre bean se fait à l'aide de l'annotation @Inject :


Listing 6. Implémentation du bean @ConfigProperties ( source )


 @ApplicationScoped class ApplicationInfoService( @Inject private val applicationInfoProperties: ApplicationInfoProperties, @Inject private val serviceClient: ServiceClient ) { ... } 

ApplicationInfoService bean ApplicationInfoService annoté par @ApplicationScoped , à son tour, est implémenté comme suit:


Listing 7. Déployer le bean @ApplicationScoped ( source )


 class ApplicationInfoResource( @Inject private val applicationInfoService: ApplicationInfoService ) 

Plus d'informations sur les contextes et l'injection de dépendances dans le guide Quarkus CDI .


Contrôleur REST


Il n'y a rien d'inhabituel dans un contrôleur REST pour ceux qui travaillent avec Spring Framework ou Java EE:


Listing 8. Contrôleur REST ( source )


 @Path("/application-info") @Produces(MediaType.APPLICATION_JSON) @Consumes(MediaType.APPLICATION_JSON) class ApplicationInfoResource( @Inject private val applicationInfoService: ApplicationInfoService ) { @GET fun get(@QueryParam("request-to") requestTo: String?): Response = Response.ok(applicationInfoService.get(requestTo)).build() @GET @Path("/logo") @Produces("image/png") fun logo(): Response = Response.ok(applicationInfoService.getLogo()).build() } 

Client REST


Pour fonctionner dans l'architecture de microservices Quarkus, un service doit pouvoir répondre aux demandes d'autres services. Étant donné que tous les services ont la même API, il est logique de créer une interface unique pour le code commun et un groupe de clients REST qui en héritent:


Listing 9. Clients REST ( code source )


 @ApplicationScoped @Path("/") interface ExternalServiceClient { @GET @Path("/application-info") @Produces("application/json") fun getApplicationInfo(): ApplicationInfo } @RegisterRestClient(baseUri = "http://helidon-service") interface HelidonServiceClient : ExternalServiceClient @RegisterRestClient(baseUri = "http://ktor-service") interface KtorServiceClient : ExternalServiceClient @RegisterRestClient(baseUri = "http://micronaut-service") interface MicronautServiceClient : ExternalServiceClient @RegisterRestClient(baseUri = "http://quarkus-service") interface QuarkusServiceClient : ExternalServiceClient @RegisterRestClient(baseUri = "http://spring-boot-service") interface SpringBootServiceClient : ExternalServiceClient 

Comme vous pouvez le voir, la création d'un client REST pour d'autres services ne fait que créer une interface avec les annotations JAX-RS et MicroProfile correspondantes.


Découverte de service


Comme vous l'avez vu dans la section précédente, les baseUri paramètres baseUri sont les noms des services. Mais pour l'instant, Quarkus n'a pas de support intégré pour Service Discovery ( Eureka ) ou il ne fonctionne pas ( Consul ), car le cadre est principalement destiné à fonctionner dans des environnements cloud. Par conséquent, le modèle de découverte de service est implémenté à l'aide de la bibliothèque Consul Client for Java .


Le client pour Consul comprend deux méthodes, register et getServiceInstance (en utilisant l'algorithme Round-robin):


Listing 10. Client à consul ( source )


 @ApplicationScoped class ConsulClient( @ConfigProperty(name = "application-info.name") private val serviceName: String, @ConfigProperty(name = "quarkus.http.port") private val port: Int ) { private val consulUrl = "http://localhost:8500" private val consulClient by lazy { Consul.builder().withUrl(consulUrl).build() } private var serviceInstanceIndex: Int = 0 fun register() { consulClient.agentClient().register(createConsulRegistration()) } fun getServiceInstance(serviceName: String): Service { val serviceInstances = consulClient.healthClient().getHealthyServiceInstances(serviceName).response val selectedInstance = serviceInstances[serviceInstanceIndex] serviceInstanceIndex = (serviceInstanceIndex + 1) % serviceInstances.size return selectedInstance.service } private fun createConsulRegistration() = ImmutableRegistration.builder() .id("$serviceName-$port") .name(serviceName) .address("localhost") .port(port) .build() } 

Vous devez d'abord enregistrer l'application:


Listing 11. Inscription auprès du Consul ( source )


 @ApplicationScoped class ConsulRegistrationBean( @Inject private val consulClient: ConsulClient ) { fun onStart(@Observes event: StartupEvent) { consulClient.register() } } 

Ensuite, vous devez convertir les noms des services en un emplacement réel. Pour ce faire, utilisez une classe qui étend ClientRequestFilter et @Provider annoté:


Listing 12. Filtre pour travailler avec Service Discovery ( source )


 @Provider @ApplicationScoped class ConsulFilter( @Inject private val consulClient: ConsulClient ) : ClientRequestFilter { override fun filter(requestContext: ClientRequestContext) { val serviceName = requestContext.uri.host val serviceInstance = consulClient.getServiceInstance(serviceName) val newUri: URI = URIBuilder(URI.create(requestContext.uri.toString())) .setHost(serviceInstance.address) .setPort(serviceInstance.port) .build() requestContext.uri = newUri } } 

Le filtre remplace simplement l'URI de l'objet requestContext par l'emplacement de service reçu du client au consul.


Test


Les tests de deux méthodes API sont implémentés à l'aide de la bibliothèque REST Assured:


Listing 13. Tests ( source )


 @QuarkusTest class QuarkusServiceApplicationTest { @Test fun testGet() { given() .`when`().get("/application-info") .then() .statusCode(200) .contentType(ContentType.JSON) .body("name") { `is`("quarkus-service") } .body("framework.name") { `is`("Quarkus") } .body("framework.releaseYear") { `is`(2019) } } @Test fun testGetLogo() { given() .`when`().get("/application-info/logo") .then() .statusCode(200) .contentType("image/png") .body(`is`(notNullValue())) } } 

Pendant les tests, il n'est pas nécessaire d'enregistrer l'application auprès de Consul, donc dans le code source du projet à côté du test se trouve ConsulClientMock , qui étend ConsulClient :


Listing 14. Mock pour ConsulClient ( source )


 @Mock @ApplicationScoped class ConsulClientMock : ConsulClient("", 0) { // do nothing override fun register() { } } 

Assemblage


Lors de la tâche de build Gradle, la tâche quarkusBuild est quarkusBuild . Par défaut, il génère un JAR de runner et un dossier lib où se trouvent les dépendances. Pour créer un uber-JAR, la tâche quarkusBuild être configurée comme suit:


Listing 15. Configuration de la génération uber-JAR ( source )


 tasks { withType<QuarkusBuild> { isUberJar = true } } 

Pour construire, exécutez ./gradlew clean build à la racine du projet.


Lancement


Avant de démarrer le microservice, vous devez démarrer Consul (décrit dans l' article principal ).


Le microservice peut être démarré en utilisant:


  • quarkusDev Gradle quarkusDev
    Exécutez à la racine du projet:
    ./gradlew :quarkus-service:quarkusDev
    ou exécutez la tâche dans l'IDE
  • uber-jar
    Exécutez à la racine du projet:
    java -jar quarkus-service/build/quarkus-service-1.0.0-runner.jar

Vous pouvez maintenant utiliser l'API REST, par exemple, exécuter la requête suivante:


GET http://localhost:8084/application-info


Le résultat sera:


Listing 16. Le résultat de l'appel API


 { "name": "quarkus-service", "framework": { "name": "Quarkus", "releaseYear": 2019 }, "requestedService": null } 

Compatibilité ressort


Le cadre offre une compatibilité avec plusieurs technologies Spring: DI , Web , Sécurité , Data JPA .


Conclusion


L'article examine un exemple de création d'un service REST simple sur Quarkus à l'aide de Kotlin et Gradle. Dans l' article principal, vous pouvez voir que l'application résultante a des paramètres comparables à ceux des applications sur d'autres frameworks JVM modernes. Ainsi, Quarkus a de sérieux concurrents tels que Helidon MicroProfile, Micronaut et Spring Boot (si nous parlons de frameworks fullstack). Par conséquent, je pense que nous attendons un développement intéressant d'événements qui seront utiles à l'ensemble de l'écosystème Java.


Liens utiles



PS Merci à vladimirsitnikov pour son aide dans la préparation de l'article.

Source: https://habr.com/ru/post/fr484320/


All Articles