En este artículo les escribiré acerca de los tres cambios más importantes que se introdujeron en Eclipse MicroProfile 3.0 y que fue liberado el 11 de Junio de este año.
Los tres API que cambiaron con respecto a MicroProfile 2.2 son los siguiente:
- Health Check que pasa a la version 2.0
- Metrics que pasa a la versión 2.0
- Rest Client que pasa a la versión 1.3
Los dos primeros han introducido cambios que no son compatibles con MicroProfile 2.2; por lo cual, sí se va a migrar a esta versión, se hace necesario realizar ajustes a nuestro código.
Seguidamente les explicaré los cambios más importantes que se introdujeron.
Health Check 2.0
En este API se hicieron cambios importantes en la respuesta de un Health Check
y que es incompatible con la versión 2.2. Además se agregó soporte para las anotaciones @Liveness
y @Readiness
y se marcaron como deprecated
varias anotaciones, como por ejemplo @Health
Se podrían preguntar ¿Qué significa Liveness y Readiness?
En el contexto de soluciones de orquestación de microservicios, como Kubernetes, una sonda de Liveness
se usa para determinar cuando se debe reiniciar un container
. A manera de ilustración, una sonda de este tipo puede determinar que se tiene un deadlock y que aunque la aplicación esta corriendo está no hace progresos; o que está consumiendo un amplio porcentaje de memoria, u otras condiciones en donde reiniciar el contenedor
puede permitir que la aplicación este disponible de nuevo. En síntesis, la aplicación no se va a recuperar a menos que la reiniciemos.
Por otro lado, una sonda de Readiness
lo que permite saber es si el container
esta listo para comenzar a recibir tráfico. Y esto es muy importante, pues en ocasiones nuestro servicio necesita cargar algún tipo de data antes de ser operativo o depende de otros servicios al arranque y este tipo de sonda nos permite manejar esas situaciones.
Ahora bien, a nivel de código el extracto de nuestro pom
relevante sería el siguiente:
<dependency>
<groupId>org.eclipse.microprofile</groupId>
<artifactId>microprofile</artifactId>
<version>3.0</version>
<type>pom</type>
<scope>provided</scope>
</dependency>
Ahora, para poder crear estas sondas vamos a hacer lo siguiente:
@Readiness
@ApplicationScoped
public class DemoReadinessCheck implements HealthCheck {
@Override
public HealthCheckResponse call() {
return HealthCheckResponse.named("Readiness")
.withData("Data 1", "Disponible").up().build();
}
}
En este caso hacemos la anotación @Readiness
y nuestra clase implementa HealthCheck
y sobreescribimos el método call
. En este ejemplo simplificado respondemos con un up
y podemos agregar diferente información como parte del payload de respuesta.
Por otro lado, para la sonda de liveness
podemos hacer lo siguiente.
@Liveness
@ApplicationScoped
public class DemoLivenessCheck implements HealthCheck {
@Override
public HealthCheckResponse call() {
return HealthCheckResponse.named("Liveness")
.withData("Dato 1", "Valor X")
.withData("Dato 2", "Valor Y")
.state(true).build();
}
}
Como se ve, es posible tener clases separadas para cada una de las sondas o una sola clase que tenga ambas anotaciones.
Si ejecutamos nuestro servicio, veremos que se expone ahora de manera automática un url /health, y si lo accesamos tenemos la siguiente respuesta:
{
"checks": [{
"data": {
"Data 1": "Disponible"
},
"name": "Readiness",
"status": "UP"
}, {
"data": {
"Dato 2": "Valor Y",
"Dato 1": "Valor X"
},
"name": "Liveness",
"status": "UP"
}],
"status": "UP"
}
Notarán que en el JSON se observa el resultado de ambas sondas; además podemos consultar cada una de manera independiente por medio del URL /health/live
{
"checks": [{
"data": {
"Dato 2": "Valor Y",
"Dato 1": "Valor X"
},
"name": "Liveness",
"status": "UP"
}],
"status": "UP"
}
y /health/ready
{
"checks": [{
"data": {
"Data 1": "Disponible"
},
"name": "Readiness",
"status": "UP"
}],
"status": "UP"
}
Metrics 2.0
En este API los cambios más sustanciales tienen que ver con el manejo de todos los contadores. A partir de ahora todos ellos son de tipo monotonic
y los que anteriormente eran non monotic
pasan a ser de tipo ConcurrentGauges
. Podemos decir -en nuestro contexto- que un contador es monotonic
cuando incrementa un valor numérico, como por ejemplo las invocaciones a un método. El comportamiento de los contadores de la versión previa se logra con @ConcurrentGauges
.
Por este y otros factores es que se dice que Metrics 2.0 implica cambios en el código y no es compatible con las versiones previas de MicroProfile.
Dicho esto, para poder tener métricas de nuestro servicio podemos hacer lo siguiente para contar con un contador:
@Counted(name = "conteoAccesos",
absolute = true,
description = "Numero de veces que es invocado el metodo acceso")
public void acceso() {
// nuestra lógica
}
Y para crear uno del tipo Gauge
sería de la siguiente manera:
@Gauge(unit = MetricUnits.NONE,
name = "totalProductos",
absolute = true,
description = "Cantidad total de productos")
public int getTotal() {
// nuestra lógica
return unValor;
}
Con lo anterior, ya podemos tener métricas en el formato de OpenMetrics
conocido anteriormente como formato Prometheus. En nuestro servicio ya tenemos un url /metrics provisto de manera automática y en donde presentamos un extracto de la información que nos brinda.
# TYPE base_jvm_uptime_seconds gauge
# HELP base_jvm_uptime_seconds Displays the start time of the Java virtual machine in milliseconds. This attribute displays the approximate time when the Java virtual machine started.
base_jvm_uptime_seconds 211.91
# TYPE base_classloader_unloadedClasses_total counter
# HELP base_classloader_unloadedClasses_total Displays the total number of classes unloaded since the Java virtual machine has started execution.
base_classloader_unloadedClasses_total 12
# TYPE application_conteoAccesos_total counter
# HELP application_conteoAccesos_total Numero de veces que es invocado el metodo acceso
application_conteoAccesos_total 15
# TYPE application_totalProductos gauge
# HELP application_totalProductos Cantidad total de productos
application_inventorySizeGuage 25
Observen que en la parte inferior están presentes las métricas de nuestro aplicativo. También podemos hacer uso del url /metrics/application para tener únicamente las métricas de nuestra aplicación.
# TYPE application_conteoAccesos_total counter
# HELP application_conteoAccesos_total Numero de veces que es invocado el metodo acceso
application_conteoAccesos_total 15
# TYPE application_totalProductos gauge
# HELP application_totalProductos Cantidad total de productos
application_inventorySizeGuage 25
Con este URL podemos alimentar a Prometheus y graficar nuestras métricas con herramientras como Grafana.
RestClient 1.3
Esta nueva versión nos permite:
- Tener soporte SSL a través de métodos nuevos en
RestClientBuilder
y propiedades de configuración. Por ejemplo podemos hacer lo siguiente:
KeyStore myStore = leerKeyStore();
RestCientBuilder.newBuilder()
.trustStore(myStore);
De esta forma podemos usar de una manera sencilla y trust store
diferente del provisto por el JVM. Inclusive podemos definirlos por medio de propiedades de MicroProfile Config
.
- Permitir que los
proxy client
puedan serCloseable/AutoClosable
. Ahora podemos hacer que una instancia de un cliente puede ser cerrada al hacer uncasting
aCloseable
oAutoCloseable
si usamos untry con recursos
, a modo de ilustración:
MiCliente client = RestClientBuilder.newBuilder()
.baseUri(miUri)
.build(MiCliente.class);
client.metodo(); // Un método que invocamos
((Closeable) client).close();
o bien:
MiCliente client = RestClientBuilder.newBuilder()
.baseUri(miUri)
.build(MiCliente.class);
try (MiCliente x = client) {
x.metodo(); // Un método que invocamos
}
- Simplificación de configuración por medio de
configKeys
. En este caso podemos hacer uno de atributos de configuración para definir elementos como el URL, URI, scope, entre otros.@RegisterRestClient(configKey="miServicio") public interface MiCliente { @GET @Path("/metodo") Responde miMetodo(); }
Y las propiedades se conformarían como las siguiente:
miServicio/mp-rest/url
miServicio/mp-rest/uri
miServicio/mp-rest/scope
- Y por último definir que
MediaType
default seaapplication/json
cuando no tenemos ningún@Consumes
o@Produces
explícito.
Conclusión
Espero que este artículo les brinde un panorama general de las cosas nuevas que nos brinda MicroProfile 3.0; y que a la fecha tiene soporte en OpenLiberty y Thorntail V2.
Comentarios