Java

18 set, 2019

Como entregar aplicações Java com mais eficiência usando Quarkus [Parte 1]

Publicidade

Cada dia mais o Quarkus tem me surpreendido, então nada melhor que compartilhar um pouco sobre ele.

Quarkus é um framework desenvolvido pela RedHat, personalizado para GraalVM e OpenJDK utilizando o HotSpot como Java Virtual Machine (JVM), sendo possível usar as versões 8 e 11 do OpenJDK (respectivamente 8u222-b10 e 11.0.4+11 encontre elas aqui).

Com ele podemos usar Java e/ou Kotlin como linguagem de programação para desenvolvermos nossas aplicações.

Mas antes, vamos as regras: A proposta aqui é fazermos uma série de posts, mostrando como desenvolver uma aplicação utilizando Quarkus e suas extensões já desenvolvidas quem facilitam a nossa vida. Nesse primeiro, iremos explicar um pouco sobre o que o é o Quarkus e seu funcionamento, além de um hello word supersônico.
Já nos próximos, iremos fazer uma PoC um pouco mais elaborada e ir desenvolvendo de forma incremental chegando até a parte de distribuir nossa aplicação em um container gerando imagem nativa.

Quarkus é incrivelmente rápido em tempo de inicialização, além de utilizar pouca memória, oferecendo uma performance satisfatória em relação aos seus concorrentes.
Quarkus utiliza uma técnica chamada Ahead-of-time compilation (AOT compilation), basicamente isso significa que o Quarkus irá compilar antecipadamente o seu código, um dos fatores que fazem ele ser incrivelmente rápido quando ao tempo de inicialização.

Um pouco mais sobre Ahead-of-time compilation (AOT compilation):
À técnica AOT visa compilar o código antes de executar, ajudando a eliminar sobrecargas de compilação durante a execução da aplicação. A diferença está em como isto é feito. Basicamente transforma bytecode da máquina virtual (VM) em código de máquina.
Isso se difere do que a nossa amada JVM faz, que é executar o bytecode Java e compilar o código freqüentemente executado para o código nativo, usando a técnica de compilação Just-in-Time (JIT) que por sua vez utiliza heurísticas para saber quando fazer isso (o que daria outro post, só com a comparação entre essas duas técnicas).

Com Quarkus conseguimos tanto desenvolver aplicações de forma imperativa quanto de forma reativa, utilizando o Vert.x, isso traz o melhor dos dois mundos e não paramos por aí. Quarkus foi feito sob-medida para aplicações cloud-native, ou seja, nativo para a computação em nuvem.

Podemos dizer que o Quarkus veio para despedaçar o paradigma de que java é lento, pesado e que não atende as exigências do mercado atual. Com ele é possível executar aplicações de forma incrivelmente rápida e que atendem as demandas atuais de arquitetura e desempenho. Segue abaixo uma imagem de comparação retirada do próprio site o quarkus.io

Imagem 1 — fonte quarkus.io

Outro ponto que precisamos abordar nesse primeiro post é a GraalVM que em conjunto com a técnica AOT compilation é o que deixa as coisas mais interessantes.
Com essa dupla não somente é possível compilar Java para código de máquina, utilizando instruções do Sistema Operacional (SO), como também conseguimos compilar para uma imagem de container sendo possível executar nossas aplicações em qualquer lugar, lindo não?!

Imagem 2 — fonte imgflip.com

Outro ponto bem interessante sobre o Quarkus é que ele utiliza padrões enterprise como CDI, JAX-RS, Eclipse Microprofile, entre outros.
Isso facilita a adoção para times que já estão acostumados com esses padrões do mundo Java, trazendo as melhores bibliotecas que amamos, como é referenciado no site.
Segue abaixo algumas que podemos usar:

Imagem 3 — fonte quarkus.io

Mas sabemos que não existe almoço grátis, não é mesmo?! Um dos fatores que deixam o Quarkus tão rápido e leve é que ele evita usar reflection, até temos como habilitar essa opção para ele começar a ler os metas dados, porém isso trás uma perda de desempenho. Então ainda existem algumas libs que não foram preparadas para a utilização com ele, mas o que temos hoje, podemos ter a certeza que atende 99,9% das aplicações que normalmente desenvolvemos.
No Quarkus essas libs são chamadas de extensões, elas são as dependências do nosso projeto, além de fornecer as informações necessárias e corretas para a GraalVM compilar nativamente nossas aplicações.
Segue abaixo a lista das extensões já disponíveis:

Para sabermos o que está disponível basta abrir o terminal digitar:
$ mvn quarkus:list-extensions ou $ gradle list-extensions
(dependendo se estamos utilizando Maven ou Gradle como gerenciador) ou também é possível achar no link: quarkus.io/extensions/

Figura 4 — fonte screenshot saída terminal utilizando mvn quarkus:list-extensions

Outra coisa que é sensacional no Quarkus é o live reload, além de ser algo muito útil para o desenvolvimento ele é impressionantemente veloz.
Chega de precisar compilar o projeto, parar servidor e subir por ter feito uma alteração em alguma linha de código.
Quando executamos o Quarkus usando como parâmetro ‘quarkus:dev’ ele irá subir em modo de desenvolvimento e a cada request HTTP o Quarkus irá fazer uma leitura em todos os arquivos para ver se existe alguma mudança, caso exista algo novo em algum arquivo ele será compilado e publicado de forma transparente, liberando o fluxo da requisição HTTP.
Isso possibilita estarmos programando em uma janela e estar com browser fazendo requests em outra.
Muito produtivo, não acha?! Além de ser zero configuração e muito veloz.


Mas vamos ao que interessa show me the code.
Para demostrar como é simples a utilização do Quarkus vamos criar o nosso gloriosos Hello Word, mas calma, como falamos na introdução será o Hello Word mais supersônico do mundo.
O que veremos nesse pequeno exemplo:

1 – Como criar nossa aplicação;
2 – Abrir ela em nosso editor ou IDE favoritos;
3 – Criar um endpoint com JAX-RS;
4 – Injeção de beans;
5 – Subir a aplicação em localhost;
6 – Live reload em ação.

Link do projeto no github aqui.

Vamos atacar o primeiro passo, que é criar a nossa aplicação.
Para isso vamos utilizar um archetype maven.

Archetype é um template para criação da estrutura de um projeto, através deste template o maven pode criar pastas, subpastas, arquivos e dependências com um único comando.

Para criar o nosso projeto, abra o terminal e digite/copie as seguintes instruções:

mvn io.quarkus:quarkus-maven-plugin:0.19.1:create \
 -DprojectGroupId=br.com.rodrigofreitas \
 -DprojectArtifactId=meuapp \
 -DclassName="br.com.rodrigofreitas.meuapp.resource.HelloWordResource" \
 -Dpath="/hello"
Figura 5 — fonte GitHub Gist

Até agora nada de novo né?! Bom se tudo ocorreu como esperado esta deverá ser a saída do nosso terminal:

Figura 6— fonte screenshot saída terminal utilizando archetype maven

Agora chegou a hora de ir para o próximo passo que é abrir o nosso projeto no editor ou IDE de tua preferência. Por falar nisso podemos usar a que mais gostamos para programar usando o Quarkus, pois como ele utiliza Maven ou Gradle para gerir as dependências e builds é possível importar na maioria dos editores e IDE que conhecemos, tais como: VSCode, Atom, Eclipse entre outros.

Nesse exemplo eu utilizarei o IntelliJ IDEA na sua versão ultimate.
Mas calma, caso tu não tenha ou não queira baixar, no final do post terá um link de um micro tutorial bônus explicando brevemente como abrir o projeto utilizando o VSCode.
Para abrir utilizando o IDEA basta seguir os passos descritos no screenshot abaixo:

Figura 7— fonte screenshot abrir um projeto existente no IntelliJ IDEA ultimate

Vemos conforme screenshot acima que o nome do nosso projeto ficou “meuapp”, como setamos no “-DprojectArtifactId” do nosso archetype maven.

Após o IDEA importar as libs e configurações descritas no nosso pom.xml esta será a estrutura que teremos no nosso Hello Word supersônico:

Figura 8— fonte screenshot estrutura do projeto no IntelliJ IDEA ultimate

Como o screenshot acima nos mostra, o archetype maven além de criar a estrutura do projeto e a classe que pedimos dentro do pacote que passamos, ele também cria alguns arquivos bem interessante para nós desenvolvedores, tais como: tests, application.properties, DockerFile para rodar na JVM e um DockerFile para rodar de forma nativa, o que veremos melhor na parte 2 dessa série de post.

Ao abrir a nossa classe HelloWordResource teremos:

package br.com.rodrigofreitas.meuapp.resource;

import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;

@Path("/hello")
public class HelloWordResource {

    @GET
    @Produces(MediaType.TEXT_PLAIN)
    public String hello() {
        return "hello";
    }
}
Figura 9— fonte GitHub Gist

Como podemos ver acima o archetype maven também já configurou o Path como setado, além de ter nos dado um método hello.
Uma observação é que com Quarkus, não há necessidade de criar uma classe Application, podemos criar caso seja nosso desejo, mas é opcional.
Além disso, apenas uma instância do recurso é criada e não uma por request. Podemos configurar isso usando as diferentes anotações de escopo, como por exemplo: ApplicationScoped, RequestScoped, etc.

Com o que temos já podemos subir a nossa aplicação abrindo o terminal e utilizando o seguinte comando na pasta raiz do projeto:
./mvnw compile quarkus:dev:

Se tudo ocorrer como esperado esta será a tua saída no terminal:

Figura 10— fonte screenshot saída do comando ./mvnw compile quarkus:dev:

Analisando a figura acima, vemos que o item grifado como 1 é a informação de quanto tempo e a porta onde a nossa aplicação foi startada (rápido né?! mas como se trata de um simples Hello Word não vamos analisar isso agora), no 2 temos a informação das as libs que estamos usando.
Agora abrindo nosso browser e digitando http://localhost:8080/hello teremos a seguinte saída:

Figura 11— fonte screenshot saída do browser

Agora vamos fazer uma pequena alteração em nosso código para vermos o live reload em ação
Vamos mudar o texto de retorno do nosso método hello de: “hello” para “Seja bem-vindo ao quarkus” na nossa classe HelloWordResource como abaixo:

package br.com.rodrigofreitas.meuapp.resource;

import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;

@Path("/hello")
public class HelloWordResource {

    @GET
    @Produces(MediaType.TEXT_PLAIN)
    public String hello() {
        return "Seja bem-vindo ao quarkus";
    }
}
Figura 12 — fonte GitHub Gist

Não precisamos parar nada, apenas salvar o arquivo editado e apertar F5 no nosso navegador que o Quarkus fará o fluxo de verificação que explicamos alguns parágrafos acima.
Logo o nosso resultado será esse:

Figura 13— fonte screenshot saída do browser

Para finalizar e vermos a implementação de injeção de dependência em ação, que no Quarkus é baseada no CDI, vamos fazer as seguintes alterações:

Criaremos um pacote chamado service onde teremos a classe HelloWordService, anotaremos ela com o ApplicationScoped.
Dentro dessa classe teremos um método, nele receberemos um parâmetro que será uma String, teremos um retorno que será esse parâmetro concatenado com uma frase.
Já na classe HelloWordResource, injetaremos a HelloWordService e teremos um novo método que recebe uma String como parâmetro da chamada HTTP e faz a chamada para o método que criamos no HelloWordService.

Com base nessas mudanças a nova estrutura do nosso projeto deverá ficar assim:

Figura 14— fonte screenshot estrutura do projeto alterado no IntelliJ IDEA ultimate

Já a nossa nova classe HelloWordService deverá ficar da seguinte maneira:

package br.com.rodrigofreitas.meuapp.service;

import javax.enterprise.context.ApplicationScoped;

@ApplicationScoped
public class HelloWordService {

    public String getHelloWithName(String name) {
        return "Seja bem-vindo ao quarkus, sr: " + name;
    }

}

E a nossa classe HelloWordResource com a alteração proposta assim:

package br.com.rodrigofreitas.meuapp.resource;

import br.com.rodrigofreitas.meuapp.service.HelloWordService;

import javax.inject.Inject;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;

@Path("/hello")
public class HelloWordResource {

    @Inject
    HelloWordService helloWordService;


    @GET
    @Path("/{name}")
    @Produces(MediaType.TEXT_PLAIN)
    public String helloWithName(@PathParam("name") String name) {
        return helloWordService.getHelloWithName(name);
    }

    @GET
    @Produces(MediaType.TEXT_PLAIN)
    public String hello() {
        return "Seja bem-vindo ao quarkus";
    }
    
}

Bom, agora a nossa aplicação está preparada para receber uma request do tipo GET recebendo um parâmetro, esse parâmetro por sua vez será processado pela nossa nova implementação no método helloWithName, vamos testar.

Se fizermos a seguinte requisição http://localhost:8080/hello/Miyag o nosso resultado deverá ser:

Figura 17 — fonte screenshot saída do browser com as alterações

Todo isso, sem precisar restartar nosso projeto.


Bom galera para não ficar um post muito grande para uma breve introdução, vamos encerrando por aqui.
No próximo encontro veremos os seguintes itens:

1: Desenvolveremos uma PoC mais elaborada para demostrar mais funcionalidade do Quarkus;
2: Executaremos ele para mostrar o seu desempenho quanto a startup e consumo de memória;
3: Compilaremos usando a GraalVM para código nativo;
4: Iremos conversar sobre o projeto que iremos desenvolver usando algumas das extensões já existentes no Quarkus.

Então, até breve.

Figura 18— Fonte Rafael Benevides

Click aqui para o micro tutorial de como abrir o projeto usando o VSCode.

Referências:
Talk Rafael Benevides, disponível em: https://bit.ly/2OrtY2j
Site Quarkus, disponível em: https://quarkus.io
Guides Quarkus, disponível em: https://quarkus.io/guides/
OpenJDK, disponível em: https://openjdk.java.net/
Site GraalVM, disponível em: https://www.graalvm.org/
Guides GraalVM, disponível em: https://www.graalvm.org/docs/
Adoptopenjdk, disponível em: https://adoptopenjdk.net/
Maven archetypes, disponível em: https://bit.ly/2GBf0Aw
AOT compilation, disponível em: http://openjdk.java.net/jeps/295