
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:

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) {
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.