Crear un microservicio en Quarkus, Kotlin y Gradle


Introduccion


En el artículo anterior, describimos brevemente el proceso de creación de un microservicio en marcos JVM modernos, así como su comparación. Este artículo será discutido con más detalle en el Quarkus recientemente lanzado por el ejemplo de crear un microservicio usando las tecnologías mencionadas y de acuerdo con los requisitos especificados en el artículo principal. La aplicación resultante pasará a formar parte de la siguiente arquitectura de microservicios:


arquitectura objetivo


El código fuente del proyecto, como de costumbre, está disponible en GitHub .


Antes de comenzar a trabajar con un proyecto, se debe instalar lo siguiente:



Crea un nuevo proyecto


Para generar un nuevo proyecto, use Web Starter o Maven (para crear un proyecto Maven o un proyecto Gradle ). Vale la pena señalar que el marco es compatible con los lenguajes Java, Kotlin y Scala.


Adicciones


En este proyecto, Gradle Kotlin DSL se utiliza como sistema de compilación. El script de compilación debe contener:


  • complementos
    Listado 1. build.gradle.kts


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

    La resolución de la versión del complemento se realiza en settings.gradle.kts .


  • adicciones
    Listado 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") ... } 

    La información sobre la importación de Maven BOM está disponible en la documentación de Gradle .



También debe abrir algunas clases de Kotlin (son final de forma predeterminada; más información sobre la configuración de Gradle en la guía Quarkus Kotlin :


Listado 3. build.gradle.kts


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

Configuracion


El marco admite la configuración mediante propiedades y archivos YAML
(más en la guía de configuración de Quarkus ). El archivo de configuración se encuentra en la carpeta de resources y tiene este aspecto:


Listado 4. application.yaml


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

En este fragmento de código, se configuran los parámetros estándar y personalizados del microservicio. Este último se puede leer así:


Listado 5. Lectura de parámetros de aplicación personalizados ( código fuente )


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

Contenedores


Antes de comenzar a trabajar con el código, debe tenerse en cuenta que en el código fuente de la aplicación Quarkus no hay un método principal (aunque puede aparecer ).


@ConfigProperties bean @ConfigProperties de la lista anterior en otro bean se realiza utilizando la anotación @Inject :


Listado 6. Implementación del bean @ConfigProperties ( fuente )


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

ApplicationInfoService bean ApplicationInfoService anotado por @ApplicationScoped , a su vez, se implementa de la siguiente manera:


Listado 7. Despliegue del bean @ApplicationScoped ( fuente )


 class ApplicationInfoResource( @Inject private val applicationInfoService: ApplicationInfoService ) 

Más información sobre Contextos e inyección de dependencias en la guía Quarkus CDI .


Controlador REST


No hay nada inusual en un controlador REST para quienes trabajan con Spring Framework o Java EE:


Listado 8. Controlador REST ( fuente )


 @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() } 

Cliente REST


Para trabajar en la arquitectura de microservicio Quarkus, un servicio debe poder cumplir con las solicitudes de otros servicios. Como todos los servicios tienen la misma API, tiene sentido crear una interfaz única para el código común y un montón de clientes REST que la heredan:


Listado 9. Clientes REST ( código fuente )


 @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 

Como puede ver, crear un cliente REST para otros servicios es simplemente crear una interfaz con las correspondientes anotaciones JAX-RS y MicroProfile.


Descubrimiento de servicio


Como viste en la sección anterior, los baseUri parámetro baseUri son los nombres de los servicios. Pero hasta ahora, Quarkus no tiene soporte incorporado para Service Discovery ( Eureka ) o no funciona ( Consul ), porque el marco está dirigido principalmente a trabajar en entornos de nube. Por lo tanto, el patrón Service Discovery se implementa utilizando la biblioteca Consul Client para Java .


El cliente para Consul incluye dos métodos, register y getServiceInstance (usando el algoritmo Round-robin):


Listado 10. Cliente a cónsul ( fuente )


 @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() } 

Primero debes registrar la aplicación:


Listado 11. Registrarse con Cónsul ( fuente )


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

A continuación, debe convertir los nombres de los servicios a una ubicación real. Para hacer esto, use una clase que extienda ClientRequestFilter y anote @Provider :


Listado 12. Filtro para trabajar con Service Discovery ( fuente )


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

El filtro simplemente reemplaza el URI del objeto requestContext con la ubicación del servicio recibida del cliente al Cónsul.


Prueba


Las pruebas para dos métodos API se implementan utilizando la biblioteca REST Assured:


Listado 13. Pruebas ( fuente )


 @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())) } } 

Durante las pruebas, no es necesario registrar la aplicación con Consul, por lo tanto, en el código fuente del proyecto al lado de la prueba está ConsulClientMock , que extiende ConsulClient :


Listado 14. Simulación para ConsulClient ( fuente )


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

Asamblea


Durante la tarea de build Gradle, se quarkusBuild tarea quarkusBuild . Por defecto, genera un JAR de corredor y una carpeta lib donde se encuentran las dependencias. Para crear un uber-JAR, la tarea quarkusBuild configurarse de la siguiente manera:


Listado 15. Configuración de la generación uber-JAR ( fuente )


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

Para compilar, ejecute ./gradlew clean build en la raíz del proyecto.


Lanzamiento


Antes de iniciar el microservicio, debe iniciar Consul (descrito en el artículo principal ).


El microservicio puede iniciarse usando:


  • Tarea Gradle quarkusDev
    Ejecutar en la raíz del proyecto:
    ./gradlew :quarkus-service:quarkusDev
    o ejecutar la tarea en el IDE
  • uber-jar
    Ejecutar en la raíz del proyecto:
    java -jar quarkus-service/build/quarkus-service-1.0.0-runner.jar

Ahora puede usar la API REST, por ejemplo, ejecute la siguiente consulta:


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


El resultado será:


Listado 16. El resultado de la llamada API


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

Compatibilidad de primavera


El marco proporciona compatibilidad con varias tecnologías Spring: DI , Web , Seguridad , Data JPA .


Conclusión


El artículo examinó un ejemplo de creación de un servicio REST simple en Quarkus usando Kotlin y Gradle. En el artículo principal, puede ver que la aplicación resultante tiene parámetros comparables con las aplicaciones en otros marcos JVM modernos. Por lo tanto, Quarkus tiene competidores serios como Helidon MicroProfile, Micronaut y Spring Boot (si hablamos de frameworks fullstack). Por lo tanto, creo que estamos esperando un desarrollo interesante de eventos que será útil para todo el ecosistema de Java.


Enlaces utiles



PD: Agradezca a vladimirsitnikov por su ayuda en la preparación del artículo.

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


All Articles