Criando um microsserviço no Quarkus, Kotlin e Gradle


1. Introdução


No artigo anterior, descrevemos brevemente o processo de criação de um microsserviço nas estruturas modernas da JVM, bem como sua comparação. Este artigo será discutido em mais detalhes no Quarkus lançado recentemente pelo exemplo da criação de um microsserviço usando as tecnologias mencionadas e de acordo com os requisitos especificados no artigo principal. O aplicativo resultante se tornará parte da seguinte arquitetura de microsserviço:


arquitetura de destino


O código fonte do projeto, como sempre, está disponível no GitHub .


Antes de iniciar o trabalho com um projeto, é necessário instalar o seguinte:



Crie um novo projeto


Para gerar um novo projeto, use o web starter ou o Maven (para criar um projeto Maven ou Gradle ). Vale ressaltar que o framework suporta as linguagens Java, Kotlin e Scala.


Vícios


Neste projeto, o Gradle Kotlin DSL é usado como o sistema de construção. O script de construção deve conter:


  • plugins
    Lista 1. build.gradle.kts


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

    A resolução da versão do plug-in é feita em settings.gradle.kts .


  • vícios
    Listagem 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") ... } 

    Informações sobre a importação da lista técnica do Maven estão disponíveis na documentação do Gradle .



Você também precisa abrir algumas classes do Kotlin (elas são final por padrão; mais informações sobre a configuração do Gradle no guia Quarkus Kotlin :


Listagem 3. build.gradle.kts


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

Configuração


A estrutura suporta configuração usando propriedades e arquivos YAML
(mais no guia de configuração do Quarkus ). O arquivo de configuração está localizado na pasta de resources e fica assim:


Listagem 4. application.yaml


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

Nesse fragmento de código, os parâmetros padrão e personalizados do microsserviço são configurados. O último pode ser lido assim:


Listagem 5. Lendo parâmetros de aplicativos customizados ( código fonte )


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

Caixas


Antes de começar a trabalhar com o código, observe que no código-fonte do aplicativo Quarkus não há método principal (embora possa aparecer ).


@ConfigProperties bean @ConfigProperties da listagem anterior em outro bean é feita usando a anotação @Inject :


Listagem 6. Implementando o bean @ConfigProperties ( origem )


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

ApplicationInfoService bean ApplicationInfoService anotado por @ApplicationScoped , por sua vez, é implementado da seguinte maneira:


Listagem 7. Implementando o bean @ApplicationScoped ( origem )


 class ApplicationInfoResource( @Inject private val applicationInfoService: ApplicationInfoService ) 

Mais informações sobre contextos e injeção de dependência no guia Quarkus CDI .


Controlador REST


Não há nada incomum em um controlador REST para quem trabalha com Spring Framework ou Java EE:


Lista 8. Controlador REST ( origem )


 @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 trabalhar na arquitetura de microsserviço Quarkus, um serviço deve poder atender a solicitações de outros serviços. Como todos os serviços têm a mesma API, faz sentido criar uma única interface para código comum e vários clientes REST que a herdam:


Lista 9. Clientes REST ( código fonte )


 @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 você pode ver, a criação de um cliente REST para outros serviços está apenas criando uma interface com as anotações JAX-RS e MicroProfile correspondentes.


Descoberta de serviço


Como você viu na seção anterior, os baseUri parâmetro baseUri são os nomes dos serviços. Mas, até agora, o Quarkus não possui suporte interno para Service Discovery ( Eureka ) ou não funciona ( Consul ), porque a estrutura é destinada principalmente ao trabalho em ambientes em nuvem. Portanto, o padrão Service Discovery é implementado usando a biblioteca Consul Client for Java .


O cliente do Consul inclui dois métodos, register e getServiceInstance (usando o algoritmo Round-robin):


Listagem 10. Cliente para cônsul ( origem )


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

Primeiro você precisa registrar o aplicativo:


Listagem 11. Registrando com o Consul ( origem )


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

Em seguida, você precisa converter os nomes dos serviços em um local real. Para fazer isso, use uma classe que estenda ClientRequestFilter e anote @Provider :


Listagem 12. Filtro para trabalhar com Service Discovery ( origem )


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

O filtro simplesmente substitui o URI do objeto requestContext pelo local do serviço recebido do cliente para o Consul.


Teste


Testes para dois métodos de API são implementados usando a biblioteca REST Assured:


Lista 13. Testes ( origem )


 @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 o teste, não há necessidade de registrar o aplicativo no Consul, portanto, no código-fonte do projeto ao lado do teste está o ConsulClientMock , que estende o ConsulClient :


Listagem 14. Mock para ConsulClient ( origem )


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

Assembléia


Durante a tarefa de build Gradle, a tarefa quarkusBuild é quarkusBuild . Por padrão, ele gera um JAR do corredor e uma pasta lib qual as dependências estão localizadas. Para criar um uber-JAR, a tarefa quarkusBuild ser configurada da seguinte maneira:


Listagem 15. Configurando a geração uber-JAR ( origem )


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

Para criar, execute ./gradlew clean build na raiz do projeto.


Lançamento


Antes de iniciar o microsserviço, você deve iniciar o Consul (descrito no artigo principal ).


O microsserviço pode ser iniciado usando:


  • quarkusDev Gradle quarkusDev
    Execute na raiz do projeto:
    ./gradlew :quarkus-service:quarkusDev
    ou execute a tarefa no IDE
  • uber-jar
    Execute na raiz do projeto:
    java -jar quarkus-service/build/quarkus-service-1.0.0-runner.jar

Agora você pode usar a API REST, por exemplo, execute a seguinte consulta:


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


O resultado será:


Listagem 16. O resultado da chamada da API


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

Compatibilidade com molas


A estrutura fornece compatibilidade com várias tecnologias Spring: DI , Web , Segurança , Data JPA .


Conclusão


O artigo examinou um exemplo de criação de um serviço REST simples no Quarkus usando Kotlin e Gradle. No artigo principal, você pode ver que o aplicativo resultante possui parâmetros comparáveis ​​com os aplicativos em outras estruturas modernas da JVM. Portanto, a Quarkus possui concorrentes sérios, como Helidon MicroProfile, Micronaut e Spring Boot (se estamos falando sobre estruturas de fullstack). Portanto, acho que estamos aguardando um desenvolvimento interessante de eventos que serão úteis para todo o ecossistema Java.


Links úteis



PS Agradeço a vladimirsitnikov pela ajuda na preparação do artigo.

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


All Articles