lunes, 2 de noviembre de 2020

Apache Camel + Quarkus

 Hoy vamos a ver como integrar Apache Camel con Quarkus. Y es que Apache Camel cuenta con diferentes proyectos y hasta ahora siempre lo habiamos visto en su versión con Spring Boot, pero hoy veremos el proyecto donde se despliega sobre Quarkus y tiene sus propios componentes. 

Para empezar explicaremos un poco que es Quarkus.  Es un Framework Java diseñado para ser desplegado como aplicación nativa de Kubernetes, es decir pensado especialmente para su despliegue en la nube a través de contenedores. Ideado también para ser utilizado por OpenJDK HotSpot y GraalVM. 

Así a simple vista, podemos pensar que es otro framework más para ser desplegado sobre otra JVM, y que con nuestra JVM habitual y Spring no necesitamos nada más. Pero tenemos que hacer hincapié en que es altamente eficaz, de ahí su gran interés. Permitiéndonos rendimientos y consumos de memoría mucho mejores que con nuestra JVM o Framework habitual. En este post veremos como instalarlo y al final haremos unas pruebas de su rendimiento comparándolo con una versión de Apache Camel con Spring. 

Podemos hacerlo funcionar en una JVM 8 u 11, pero es mejor si instalamos GraalVM. Una vez que tengamos lista la JVM, empezaremos con la creación de nuestro proyecto. 

Nuestro proyecto heradará de camel-quarkus-bom:1.1.0, tendrá las librerías de SQL para la conexión a BBDD y las de Rest y Netty para la conexión mediante HTTP. Uno de los principales inconvenientes a la hora de realizar un proyecto con Apache Camel + Quarkus, es que los componentes y la documentación no están tan testeados o elaborados, como puede pasar en comparación con los de Spring Boot. 

<parent>
    <groupId>org.apache.camel.quarkus</groupId>
    <artifactId>camel-quarkus-bom</artifactId>
    <version>1.1.0</version>
</parent>
<dependencies>
    <!-- start -->
    <dependency>
      <groupId>org.apache.camel.quarkus</groupId>
      <artifactId>camel-quarkus-main</artifactId>
    </dependency>
    <!-- HTTP -->
    <dependency>
      <groupId>org.apache.camel.quarkus</groupId>
      <artifactId>camel-quarkus-netty-http</artifactId>
    </dependency>
    ...
</dependencies>

El código será similar al de Apache Camel. Tendremos una clase que aglutinará los Route que desarrollemos. Y tendremos un fichero de aplicación donde indicaremos aspectos comunes y el dataSource. 

@ApplicationScoped
public class BookRouter extends RouteBuilder {
    @Override
    public void configure() throws Exception {
        restConfiguration().component("netty-http").port(8099).bindingMode(RestBindingMode.json);

        rest("/book").get().route().to("sql:SELECT ID, NAME, AUTHOR FROM BOOK").log("--- select all books. Body: ${body} ---");

        rest("/book/{id}").get().route().to("sql:SELECT ID, NAME, AUTHOR FROM BOOK WHERE ID = :#id").log("--- 2 select a book ${body} ---");

        rest().post("book").route().routeId("postBookRoute")
        .to("sql:INSERT INTO BOOK (NAME, AUTHOR) VALUES (:#name, :#author)").setHeader(Exchange.HTTP_RESPONSE_CODE, constant(201))
        .setBody(constant(null));

        rest().delete("book/{id}").route().to("sql:DELETE FROM BOOK WHERE ID = :#id")
        .setHeader(Exchange.HTTP_RESPONSE_CODE, constant(200)).setBody(constant(null));
        //need camel-bean
        rest().put("book/{id}").route().choice().when()
        .simple("${header.id} < 1").bean(BookRouter.class, "negativeId").otherwise()
        .to("sql:UPDATE BOOK SET NAME = :#name, AUTHOR = :#author WHERE ID = :#id").setHeader(Exchange.HTTP_RESPONSE_CODE, constant(200))
        .setBody(constant(null));
    }
    public void negativeId(final Exchange exchange) {
        exchange.getIn().setBody("{\"msg\":\"id can not be negative\"}");
        exchange.getIn().setHeader(Exchange.HTTP_RESPONSE_CODE, 400);
    }
}

Lo que no tendremos es una clase como Spring que nos permita arrancar la aplicación y debugarla fácilmente en eclipse, esta puede ser otra desventaja a la hora de desarrollar. Para arrancar la aplicación lo podremos hacer mediante comandos maven:

mvn clean compile quarkus:dev

Una vez que tenemos desarrollada nuestra aplicación, vamos a ver que tal se comporta. Y para crearemos una imagen de ambas aplicaciones, las desplegaremos en un docker-compose con Portainer y las probaremos con la utilidad Hey.

Para hacer la imagen de Docker es muy parecido a como lo realizabamos con Spring boot, la diferencia es que en este caso al crear el empaquetado tendremos una aplicación Kubernetes-nativa con sufijo runner. Para crear este empaquetado si tenemos instalado GraalVM utilizamos el comando:

mvn clean package -Pnative -Dquarkus.native.container-build=true \
 -Dquarkus.native.builder-image=quay.io/quarkus/ubi-quarkus-native-image:20.2.0-java8 \
 -Dquarkus.native.container-runtime=docker

Y ahora procederiamos a crear la imagen. Ejemplo de Dockerfile

FROM registry.access.redhat.com/ubi8/ubi-minimal
WORKDIR /work/
COPY target/*-runner /work/application
RUN chmod 775 /work
EXPOSE 8099
CMD ["./application", "-Dquarkus.http.host=0.0.0.0"]

Ahora procedemos a arrancar nuestro docker-compose y ejecutar el siguiente comando de Hey. 

hey -n 1000 -m GET -T "application/json" http://localhost:8099/book/1

Sobre quarkus Hey nos da estos resultados: Average: 0.0757 secs

Y Portainer estos otros: 200 MB de Memoría y consumos mínimos de CPU

Si ejecutamos lo mismo sobre una aplicación Apache Camel + Spring Boot. Hey nos da estos resultados: Average: 0.1712 secs , bastante mas que Quarkus

Y Portainer estos otros: 400 MB de Memoria y consumos de CPU también bajos pero mucho más altos que los de Quarkus.

Como veis, una gran diferencia. Lo suficiente como para que nos haga decantarnos por Quarkus sobre Spring Boot. Sobre todo si es una aplicación básica que no vayamos a necesitar de mucha documentación o de ayuda de los foros.

Como siempre puedes ver el código aquí

No hay comentarios:

Publicar un comentario