7. 7
Mi experiencia trabajando con Quarkus
● no hay que aprender nada nuevo
● es Java estándar
● la intuición funciona
● es menos verboso que otros frameworks
● modo de desarrollo mvn quarkus:dev
○ live reload
○ testing continuo
○ zero configuración
● lenguaje basado en estándares
○ MicroProfile
○ SmallRye
● orientado a cloud (eventos, K8s, etc…)
● muy rápido
● muy liviano
● muy fácil hacer TDD con @InjectSpy
En resumen, es muy fácil y sencillo
9. 9
AWS Lambda
AWS Lambda SnapStart Configuration
AWS Lambda with RESTEasy Reactive…
Azure Functions
Azure Functions with RESTEasy Reactive…
Container Images
Deploying on OpenShift
Deploying to Google Cloud Platform (GCP)
Deploying to Heroku
Deploying to Microsoft Azure Cloud
Funqy
Funqy AWS Lambda Binding
Funqy Google Cloud Functions
Funqy HTTP Binding (Standalone)
Funqy HTTP Binding with AWS Lambda
Funqy HTTP Binding with Azure Functions
Funqy HTTP Binding with Google Cloud Functions
Funqy Knative Events Binding
Orientado a contenedor. Cloud Native. CNCF
Google Cloud Functions (Serverless)
Google Cloud Functions (Serverless) with RESTEasy Reactive…
Dev Services for Kubernetes
Kubernetes Client
Kubernetes Config
Kubernetes extension
Reading properties from Spring Cloud Config Server
Getting Started with SmallRye Stork
Using Stork with Kubernetes
Stork Reference Guide
Quarkus: crear function as a
service con Funqy y Knative
10. 10
Arranque en JVM vs arranque en nativo
● en JVM arranca en unos 10s
● en nativo arranca en unos 200ms ¡¡¡50 veces más rápido!!!
Compilación a
Nativo
Nativo: Guía de
Referencia
12. 12
Modo desarrollo. mvn quarkus:dev
Arranca la aplicación, y se pueden hacer cambios sobre la marcha, que se
ven reflejados inmediatamente en la aplicación sin reiniciar.
Continuos testing
Cuando cambiamos código y se graba el fichero se detectan los cambios y
se pasan los test.
Configuración: %dev y %test
quarkus.datasource.db-kind=mysql
quarkus.datasource.jdbc.url=jdbc:mysql://server:3306/users
quarkus.datasource.jdbc.driver=com.mysql.cj.jdbc.Driver
%dev.quarkus.datasource.jdbc.url = jdbc:mysql://localhost:3306/users
%test.quarkus.datasource.db-kind=h2
%test.quarkus.datasource.jdbc.url=jdbc:h2:tcp://localhost/mem:test
%test.quarkus.datasource.jdbc.driver=org.h2.Driver
Getting Started
Continuous Testing
Continuous Testing y
compilación a nativo
Introducción a Quarkus
Introducción a Quarkus - II
13. 13
devServices (testContainers "automágicos")
Si incluimos una extensión y no la configuramos, Quarkus iniciará
automáticamente el servicio en modo desarrollo y pruebas.
Esto utiliza testContainers bajo el capot, y necesitas tener docker (o podman)
¡¡¡Configuración 0 para empezar a desarrollar!!!
Dev Services and UI for OpenID Connect (OIDC)
Dev Services for AMQP
Dev Services for Apicurio Registry
Dev Services for Elasticsearch
Dev Services for Infinispan
Dev Services for Kafka
Dev Services for Kubernetes
Dev Services for Pulsar
Dev Services for RabbitMQ
Dev Services for Databases
DB2 (container)
Derby (in-process)
H2 (in-process)
MariaDB (container)
Microsoft SQL Server (container)
MySQL (container)
Oracle Express Edition (container)
PostgreSQL (container)
Dev Services
14. 14
Todas las anotaciones son del
estándar de Java:
javax.ws.rs.GET
jakarta.ws.rs.GET;
1 Indicamos el Path del endpoint
2 Método GET devolverá la lista
3 Podemos obtener una fruta por su
nombre
4 y el nombre lo obtenemos como un
PathParam
5 Lo mismo para el método POST
Servicios REST
@Path("/fruits")
public class FruitResource {
private Map<String,Fruit> fruits;
@GET
public Map<String, Fruit> list() {
return fruits;
}
@GET
@Path("/{fruitName}")
public Fruit getFruit(@PathParam String name) {
return fruits.get(name);
}
@POST
public Map<String, Fruit> add(Fruit fruit) {
fruits.add(fruit.name, fruit);
return fruits;
}
}
RESTEasy Classic
1
2
3
4
5
15. 15
Todas las entidades extienden de
PanacheEntity o
PanacheEntityBase
1 Active Record prefiere propiedades
públicas
2 es como el find de Hibernate
3 ejemplo de list()
4 ejemplo de find con where
5 lo mismo para el delete
Persistencia con Panache y Active Record
@Entity
public class Fruit extends PanacheEntity {
public String name;
public Integer kcal;
public String season;
public static Fruit find(String name){
return find("name", name).firstResult();
}
public static List<Fruit> findSeason(String season){
return list("season", season);
}
public static void deleteByName(String name){
delete("name", name);
}
}
ORM Panache
1
2
3
4
5
16. 16
Uni es objeto de SmallRye Mutiny
1 la firma del método dice si es
reactivo o no. Uni<Fruit> en lugar
de Fruit
2 Multi es parecido a un stream de
Uni. Puede ir devolviendo varios a
medida que se van recuperando.
3 Hay que distinguir también entre
void y Uni<Void>
Mismo ejemplo en reactivo
@Entity
public class Fruit extends PanacheEntity {
public String name;
public Integer kcal;
public String season;
public static Uni<Fruit> find(String name){
return find("name", name).firstResult();
}
public static Multi<List<Fruit>> findSeason(String season){
return list("season", season);
}
public static Uni<Void> deleteByName(String name){
delete("name", name);
}
}
RESTEasy Reactive
1
2
3
Getting started with Reactive
SmallRye Mutiny
17. 17
@Path("/receipts")
@RegisterRestClient(configKey = "receipts-api")
public interface ReceiptsService {
@GET
Receipt getById(@QueryParam("id") Integer id);
@GET
@Path("/ingredient/{name}")
@Produces(MediaType.APPLICATION_JSON)
List<Receipt> getByIngredient(@PathParam String name);
}
La especificación de RestClient es
de las más sencillas que hay.
1 Anotamos @RegisterRestClient
para indicar que esta interfaz es la
de un servicio REST ajeno a nuestro
micro. Y con configKey le damos
un alias
Uso de RestClient
Reactive Rest Client
1
#application.properties
receipts-api/mp-rest/url=https://receipts-api.acme.com
receipts-api/mp-rest/scope=javax.enterprise.context.RequestScoped
receipts-api/mp-rest/connectTimeout=1000
receipts-api/mp-rest/readTimeout=1000
%dev.receipts-api/mp-rest/url=http://localhost:8081
1
1
Classic Rest Client
Microservicio con REST y
MongoDB
18. 18
CloudEvent es un estándar
1 inyectamos el ObjectMapper de
jackson para hacer
transformaciones
2 se recibe el evento como un POST
3 Se proyecto el CloudEvent sobre el
evento concreto que es
4 se valida
5 se invoca al servicio con el payload
Recibir CloudEvents desde Knative Event Broker
@Path("/fruit")
public class FruitResource {
@Inject
ObjectMapper mapper;
@Inject
FruitService fruitService;
@POST
public void manageFruitEvent(final CloudEvent event) {
FruitEvent payload = PojoCloudEventDataMapper
.from(mapper, FruitEvent.class)
.map(event.getData())
.getValue();
validatePayload(payload);
fruitService.manage(payload, event.getId());
}
}
CloudEvents
1
2
3
4
5
19. 19
public class EventService {
@Inject
ObjectMapper mapper;
@Inject
@RestClient
BrokerService brokerService;
public void sendEvent(final FruitEvent event) {
CloudEvent cloudEvent = CloudEventBuilder.v1()
.withSource(URI.create(“/fruit”))
.withType(“com.acme.fruit”)
.withId(UUID.randomUUID().toString())
.withDataContentType(MediaType.APPLICATION_JSON)
.withData(PojoCloudEventData
.wrap(event, mapper::writeValueAsBytes))
.build();
brokerService.send(cloudEvent);
}
}
Enviamos un CloudEvent al broker
1 inyectamos el ObjectMapper de
jackson serializar el evento que se
quiere enviar
2 inyectamos el RestClient con el
broker-ingress
3 Construimos el CloudEvent con el
POJO del evento
4 Lo enviamos como un POST a
través del brokerService
Enviar CloudEvents a Knative Event Broker
TriggerMesh
1
2
3
4
20. 20
SmallRye GraphQL sirve recursos
vía GraphQL
1 Solo por anotar con @GraphQLApi
es un endpoint GraphQL
2 inyectamos el servicio
3 Una query de consulta
4 Mutación de creación
5 Mutación de borrado
GraphQL API
@GraphQLApi
public class FruitResource {
@Inject
FruitService service;
@Query("allFruits")
@Description("Get all fruits")
public List<Fruit> getAllFruits() {
return service.getAllFruitts();
}
@Mutation
public Fruit createFruit(Fruit fruit) {
service.addFruit(fruit);
return fruit;
}
@Mutation
public Fruit deleteFruit(int id) {
return service.deleteFruit(id);
}
}
SmallRye GraphQL
1
2
4
5
3
Cliente SmallRye GraphQL
Quarkus: exponer una
interfaz GraphQL
21. 21
Microprofile: Health
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-smallrye-health</artifactId>
</dependency>
● /q/health/live - La aplicación está levantada y corriendo
● /q/health/ready - La aplicación está lista para servir request
● /q/health/started - Evento que se produce al arrancar
● /q/health - Acumula todos los health check provinientes de la aplicación
@Liveness
@ApplicationScoped
public class SimpleHealthCheck implements HealthCheck {
@Override
public HealthCheckResponse call() {
if (externalService.liveness()){
return HealthCheckResponse.up("Simple health check");
} else { … }
}
Simplemente por incluir la librería
quarkus-smallrye-health el
microservicio expone unos endpoints
con el liveness y readyness
necesarios, por ejemplo, para un
entorno de K8s.
En el bloque de abajo se pone un
ejemplo, por si nuestro microservicio
depende síncronamente de otro, y se
quiere añadir como su liveness al
nuestro, ya que dependemos de él.
SmallRye Health
22. 22
Microprofile: Metrics (sabor SmallRye Metrics)
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-smallrye-metrics</artifactId>
</dependency>
● /q/metrics - Métricas de la aplicación y los endpoints
1 Por defecto, si incluimos la librería,
se empiezan a recoger información
de todos lo endpoints, pero
podemos anotar con counted y
timed cualquier método, y definir
métricas custom
2 @timed es muy útil para tomar
métricas de cada a identificar
posibles problemas de rendimeinto
NOTA: ahora se ha deprecado en
favor de micrometer
SmallRye Metrics
@ApplicationScoped
@Path("/fruit")
public class FruitResource {
@Inject
FruitService fruitService;
@POST
@Counted(name = "ProcessFruitCount", displayName = "fruit.counted")
@Timed(name = "ProcessFruitTime", displayName = "fruit.timed")
public void processFruit (final CloudEvent event) {
FruitEvent fruitEvent = map(event);
fruitService.process(fruitEvent)
}
1
2
23. 23
Microprofile: Metrics (sabor Micrometer)
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-micrometer</artifactId>
</dependency>
o
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-micrometer-registry-prometheus</artifactId>
</dependency>
SmallRye Metrics y Micrometer satisfacen el estándar de Microprofile Metrics
De las librerías anteriores, la primera expone en /q/metrics. La segunda, además, exporta las métricas
a un colector de prometheus. Hay muchos otros posibles colectores de métricas. Sólo hay que incluir la
librería adecuada
Telemery Micrometer
● micrometer-registry-newrelic-telemetry
● micrometer-registry-otlp
● micrometer-registry-signalfx
● micrometer-registry-stackdriver
● micrometer-registry-statsd
● micrometer-registry-azure-monitor
● micrometer-registry-datadog
● micrometer-registry-graphite
● micrometer-registry-influx
● micrometer-registry-jmx
Para los desarrolladores es muy útil
tener la info en /q/metrics pero de
cara a la observavilidad es preferible
poder estudiar los datos en un
dahboard de Grafana u otro sistema
que convierta los números en
gráficos.
25. 25
@Path("/fruits")
public class FruitResource {
@POST
@Retry(maxRetries=3, delay=1000, maxDuration=5000)
@Fallback(fallbackMethod="fallbackHandleEvent")
public void add(final FruitEvent event) {
Fruit stored = Fruit.findById(event.name);
if (stored == null){
map.toEntity(event).persist();
}
}
public void fallbackHandleEvent(final FruitEvent event) {
…
}
En un entorno de microservicios es
normal implementar cierta
resiliencia ante los fallos
1 La anotación Retry permite indicar
cuantas veces, como espaciamos los
intentos, etc…
2 Podemos indicar un método de
fallback para el caso de que se
alcance el número de reintentos y no
se haya alcanzado el éxito
3 Loggear y ¿DLQ?
4 Implementa: retries, timeouts,
fallbacks y circuit breaker
FaultTolerance
SmallRye Fault Tolerance
1
3
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-smallrye-fault-tolerance</artifactId>
</dependency>
2
26. 26
RunOnVirtualThread
@Path("/fruits")
@RunOnVirtualThread
public class FruitResource {
private Map<String,Fruit> fruits;
@GET
public Map<String, Fruit> list() {
return fruits;
}
@GET
@Path("/{fruitName}")
public Fruit getFruit(@PathParam String name) {
return fruits.get(name);
}
@POST
public Map<String, Fruit> add(Fruit fruit) {
fruits.add(fruit.name, fruit);
return fruits;
}
}
Writing CRUD applications
using virtual threads
1
When Quarkus meets virtual
threads
Testing Virtual Thread
Application
Processing Kafka records on
virtual thread
Compiling virtual thread
applications into native
executables
Use virtual threads in REST
applications
Virtual thread support
reference
27. 27
Accessing application properties with Spring Boot
properties API
Use Spring Boot’s @ConfigurationProperties in
place of MicroProfile Config annotations
Extension for Spring Data API
While you are encouraged to use Hibernate ORM
with Panache for your data layer, Quarkus provides a
compatibility layer for Spring Data JPA in the form of
the spring-data-jpa extension.
Extension for Spring Data REST
Spring Data REST simplifies the creation of CRUD
applications based on our Spring Data compatibility
layer.
Quarkus Extension for Spring Cache API
While you are encouraged to use the Cache
extension for your application-level caching, Quarkus
provides a compatibility layer for Spring Cache in the
form of the spring-cache extension.
Quarkus Extension for Spring DI API
While you are encouraged to use CDI annotations for
injection, Quarkus provides a compatibility layer for
Spring dependency injection in the form of the
spring-di extension.
Quarkus: módulos para Spring
Quarkus Extension for Spring Scheduling API
While you are encouraged to use the Scheduler or Quartz
extensions to schedule tasks, Quarkus provides a
compatibility layer for Spring Scheduled in the form of the
spring-scheduled extension.
Quarkus Extension for Spring Security API
While you are encouraged to use the Quarkus Security layer
to secure your applications, Quarkus provides a
compatibility layer for Spring Security in the form of the
spring-security extension.
Quarkus Extension for Spring Web API
While you are encouraged to use Jakarta REST annotations
for defining REST endpoints, Quarkus provides a
compatibility layer for Spring Web in the form of the spring-
web extension.
Reading properties from Spring Cloud Config Server
Quarkus provides a compatibility layer for Spring Cloud
Config in the form of the spring-cloud-config-client
extension.
29. 29
Este chat es muy útil. Hay varios canales.
● #dev es para el propio desarrollo de Quarkus
● #users es para las preguntas que hacen los
usuarios. Revisa el historial, porque tu pregunta
seguramente ha sido respondida con
anterioridad y si no, planteala, y verás como
alguien pronto te ayuda.
Comunidad
STACK OVERFLOW
Las guías están muy bien documentadas y cubren el
95% de los casos. Pero en ocasiones, necesitas
preguntar sobre algún comportamiento extraño, y el tag
de quarkus es ampliamente usado en Stack Overflow.
Hay un montón de gente revisando este tag dispuesta a
ayudarte.
QUARKUS ZULIP CHAT QUARKUS DEVELOPMENT MAILING LIST
Esta lista de correo es para los propios desarrolladores
de Quarkus, no es para preguntar dudas acerca del
funcionamiento. Pero es muy interesante estar suscrito
para conocer de primera mano, las decisiones que se
toman sobre el lenguaje, o ver las novedades de una y
otra release.
32. 32
¿Qué hemos aprendido de Quarkus?
Es muy liviano y consume muy pocos recursos
Ya sabes Quarkus
Es muy sencillo
Muy agradable para el desarrollador
Una documentación muy buena
Con una gran comunidad detrás
Turno de preguntas